[LeetCode] 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.html

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.java

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.node

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

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 2post

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,而後對左右子結點分別調用遞歸便可,參見代碼以下:this

 

解法一:url

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,這樣就保證了右子結點存在就會被優先調用,參見代碼以下:spa

 

解法二:code

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;
    }
};

 

參考資料:

https://discuss.leetcode.com/topic/87069/java-recursive-solution-beats-94/2

https://discuss.leetcode.com/topic/84295/verbose-java-solution-easy-to-understand

 

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

相關文章
相關標籤/搜索