[LeetCode] 545. Boundary of Binary Tree 二叉樹的邊界


 

Given a binary tree, return the values of its boundary in anti-clockwise direction starting from root. Boundary includes left boundary, leaves, and right boundary in order without duplicate nodes.

Left boundary is defined as the path from root to the left-most node. Right boundary is defined as the path from root to the right-most node. If the root doesn't have left subtree or right subtree, then the root itself is left boundary or right boundary. Note this definition only applies to the input binary tree, and not applies to any subtrees.

The left-most node is defined as a leaf node you could reach when you always firstly travel to the left subtree if exists. If not, travel to the right subtree. Repeat until you reach a leaf node.

The right-most node is also defined by the same way with left and right exchanged.

Example 1

Input:
  1
   \
    2
   / \
  3   4

Ouput:
[1, 3, 4, 2]

Explanation:
The root doesn't have left subtree, so the root itself is left boundary.
The leaves are node 3 and 4.
The right boundary are node 1,2,4. Note the anti-clockwise direction means you should output reversed right boundary.
So order them in anti-clockwise without duplicates and we have [1,3,4,2].

 

Example 2

Input:
    ____1_____
   /          \
  2            3
 / \          / 
4   5        6   
   / \      / \
  7   8    9  10  
       
Ouput:
[1,2,4,7,8,9,10,6,3]

Explanation:
The left boundary are node 1,2,4. (4 is the left-most node according to definition)
The leaves are node 4,7,8,9,10.
The right boundary are node 1,3,6,10. (10 is the right-most node).
So order them in anti-clockwise without duplicate nodes we have [1,2,4,7,8,9,10,6,3].

 

這道題給了一棵二叉樹,讓我們以逆時針的順序來輸出樹的邊界,按順序分別為左邊界,葉結點和右邊界。題目中給的例子也很清晰的明白哪些算是邊界上的結點。那么最直接的方法就是分別按順序求出左邊界結點,葉結點,和右邊界結點。那么如何求的,對於樹的操作肯定是用遞歸最簡潔啊,所以可以寫分別三個遞歸函數來分別求左邊界結點,葉結點,和右邊界結點。首先要處理根結點的情況,當根結點沒有左右子結點時,其也是一個葉結點,那么一開始就將其加入結果 res 中,那么再計算葉結點的時候又會再加入一次,這樣不對。所以判斷如果根結點至少有一個子結點,才提前將其加入結果 res 中。然后再來看求左邊界結點的函數,如果當前結點不存在,或者沒有子結點,直接返回。否則就把當前結點值加入結果 res 中,然后看如果左子結點存在,就對其調用遞歸函數,反之如果左子結點不存在,那么對右子結點調用遞歸函數。而對於求右邊界結點的函數就反過來了,如果右子結點存在,就對其調用遞歸函數,反之如果右子結點不存在,就對左子結點調用遞歸函數,注意在調用遞歸函數之后才將結點值加入結果 res,因為是需要按逆時針的順序輸出。最后就來看求葉結點的函數,沒什么可說的,就是看沒有子結點存在了就加入結果 res,然后對左右子結點分別調用遞歸即可,參見代碼如下:

 

解法一:

class Solution {
public:
    vector<int> boundaryOfBinaryTree(TreeNode* root) {
        if (!root) return {};
        vector<int> res;
        if (root->left || root->right) res.push_back(root->val);
        leftBoundary(root->left, res);
        leaves(root, res);
        rightBoundary(root->right, res);
        return res;
    }
    void leftBoundary(TreeNode* node, vector<int>& res) {
        if (!node || (!node->left && !node->right)) return;
        res.push_back(node->val);
        if (!node->left) leftBoundary(node->right, res);
        else leftBoundary(node->left, res);
    }
    void rightBoundary(TreeNode* node, vector<int>& res) {
        if (!node || (!node->left && !node->right)) return;
        if (!node->right) rightBoundary(node->left, res);
        else rightBoundary(node->right, res);
        res.push_back(node->val);
    }
    void leaves(TreeNode* node, vector<int>& res) {
        if (!node) return;
        if (!node->left && !node->right) {
            res.push_back(node->val);
        }
        leaves(node->left, res);
        leaves(node->right, res);
    }
};

 

下面這種方法把上面三種不同的遞歸揉合到了一個遞歸中,並用 bool 型變量來標記當前是求左邊界結點還是求右邊界結點,同時還有加入葉結點到結果 res 中的功能。如果左邊界標記為 true,那么將結點值加入結果 res 中,下面就是調用對左右結點調用遞歸函數了。根據上面的解題思路可以知道,如果是求左邊界結點,優先調用左子結點,當左子結點不存在時再調右子結點,而對於求右邊界結點,優先調用右子結點,當右子結點不存在時再調用左子結點。綜上考慮,在對左子結點調用遞歸函數時,左邊界標識設為 leftbd && node->left,而對右子結點調用遞歸的左邊界標識設為 leftbd && !node->left,這樣左子結點存在就會被優先調用。而右邊界結點的情況就正好相反,調用左子結點的右邊界標識為 rightbd && !node->right, 調用右子結點的右邊界標識為 rightbd && node->right,這樣就保證了右子結點存在就會被優先調用,參見代碼如下:

 

解法二:

class Solution {
public:
    vector<int> boundaryOfBinaryTree(TreeNode* root) {
        if (!root) return {};
        vector<int> res{root->val};
        helper(root->left, true, false, res);
        helper(root->right, false, true, res);
        return res;
    }
    void helper(TreeNode* node, bool leftbd, bool rightbd, vector<int>& res) {
        if (!node) return;
        if (!node->left && !node->right) {
            res.push_back(node->val);
            return;
        }
        if (leftbd) res.push_back(node->val);
        helper(node->left, leftbd && node->left, rightbd && !node->right, res);
        helper(node->right, leftbd && !node->left, rightbd && node->right, res);
        if (rightbd) res.push_back(node->val);
    }
};

 

下面這種解法實際上時解法一的迭代形式,整體思路基本一樣,只是沒有再用遞歸的寫法,而是均采用 while 的迭代寫法,注意在求右邊界結點時迭代寫法很難直接寫出逆時針的順序,我們可以先反過來保存,最后再調個順序即可,參見代碼如下:

 

解法三:

class Solution {
public:
    vector<int> boundaryOfBinaryTree(TreeNode* root) {
        if (!root) return {};
        vector<int> res, right;
        TreeNode *l = root->left, *r = root->right, *p = root;
        if (root->left || root->right) res.push_back(root->val);
        while (l && (l->left || l->right)) {
            res.push_back(l->val);
            if (l->left) l = l->left;
            else l = l->right;
        }
        stack<TreeNode*> st;
        while (p || !st.empty()) {
            if (p) {
                st.push(p);
                if (!p->left && !p->right) res.push_back(p->val);
                p = p->left;
            } else {
                p = st.top(); st.pop();
                p = p->right;
            }
        }
        while (r && (r->left || r->right)) {
            right.push_back(r->val);
            if (r->right) r = r->right;
            else r = r->left;
        }
        res.insert(res.end(), right.rbegin(), right.rend());
        return res;
    }
};

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/545

 

類似題目:

Binary Tree Right Side View

 

參考資料:

https://leetcode.com/problems/boundary-of-binary-tree/

https://leetcode.com/problems/boundary-of-binary-tree/discuss/101288/Java-Recursive-Solution-Beats-94

https://leetcode.com/problems/boundary-of-binary-tree/discuss/101280/Java(12ms)-left-boundary-left-leaves-right-leaves-right-boundary

https://leetcode.com/problems/boundary-of-binary-tree/discuss/101294/Java-C%2B%2B-Clean-Code-(1-Pass-perorder-postorder-hybrid)

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM