1. 程式人生 > 其它 >[LeetCode] 1305. All Elements in Two Binary Search Trees 兩棵二叉搜尋樹中的所有元素

[LeetCode] 1305. All Elements in Two Binary Search Trees 兩棵二叉搜尋樹中的所有元素


Given two binary search trees root1 and root2, return a list containing all the integers from both trees sorted in ascending order.

Example 1:

Input: root1 = [2,1,4], root2 = [1,0,3]
Output: [0,1,1,2,3,4]

Example 2:

Input: root1 = [1,null,8], root2 = [8,1]
Output: [1,1,8,8]

Constraints:

  • The number of nodes in each tree is in the range [0, 5000]
    .
  • -10^5 <= Node.val <= 10^5

這道題給了兩棵二叉搜尋樹,讓返回一個包含所有結點值的子陣列,並且是有序的。最簡單暴力的方法就是分別遍歷兩棵樹,然後把得到的結點值放到一個數組裡,然後再對該陣列排序,而且也能通過 OJ。用這種方法的話就沒有利用到二叉搜尋樹的性質,用任意一種遍歷順序都可以,參見程式碼如下:


解法一:

class Solution {
public:
    vector<int> getAllElements(TreeNode* root1, TreeNode* root2) {
        vector<int> res;
        dfs(root1, res);
        dfs(root2, res);
        sort(res.begin(), res.end());
        return res;
    }
    void dfs(TreeNode* node, vector<int>& res) {
        if (!node) return;
        dfs(node->left, res);
        res.push_back(node->val);
        dfs(node->right, res);
    }
};

上面的解法並沒有利用到二叉搜尋樹的性質,可能並不是面試官想要看見的解法。二叉搜尋樹具有左子結點值小於父結點值,小於右子結點的特點,所以用中序遍歷得到的結點值是有序的。這裡分別對 root1 和 root2 進行中序遍歷,分別得到兩個有序的陣列,這樣就可以通過 merge 陣列方式來將兩個有序數組合併成一個大的有序陣列了,是線性的複雜度。下面的程式碼用的佇列而不是陣列,並沒有太大的區別,參見程式碼如下:


解法二:

class Solution {
public:
    vector<int> getAllElements(TreeNode* root1, TreeNode* root2) {
        vector<int> res;
        queue<int> q1, q2;
        dfs(root1, q1);
        dfs(root2, q2);
        while (!q1.empty() || !q2.empty()) {
            if (q2.empty() || (!q1.empty() && q1.front() <= q2.front())) {
                res.push_back(q1.front());
                q1.pop();
            } else {
                res.push_back(q2.front());
                q2.pop();
            }
        }
        return res;
    }
    void dfs(TreeNode* node, queue<int>& q) {
        if (!node) return;
        dfs(node->left, q);
        q.push(node->val);
        dfs(node->right, q);
    }
};

之前兩種解法都是遞迴的寫法,再來看一種迭代的寫法,還是用的中序遍歷,不過此時是同時中序遍歷兩棵樹,按照大小順序將結點值加入到結果 res 中,保證了最終的結果是有序的。對於迭代的中序遍歷寫法最好也是要掌握的,需要用到棧來輔助,由於是同時遍歷兩棵樹,所以這裡用兩個棧 st1 和 st2。迴圈到條件是隻要 root1,root2,st1 和 st2 有任意一個不為空,中序遍歷的順序是左根右,所以首先要做的把當前結點下方的所有左子結點壓入棧中,不停的對 root1 遍歷,只要不為空,就將結點壓入棧,然後更新為其左子結點。同理對 root2 進行相同的操作,接下倆判斷若 st2 為空,說明當前要操作 st1 中的結點,或著若 st1 不為空(此時 st2 也不為空),且 st1 的棧頂結點值小於等於 st2 的棧頂結點值時,同樣操作 st1 中的結點。否則操作 st2 中的結點,操作方法都是取出棧頂結點,將其值加入結果 res 中,然後更新為其右子結點即可,參見程式碼如下:


解法三:

class Solution {
public:
    vector<int> getAllElements(TreeNode* root1, TreeNode* root2) {
        vector<int> res;
        stack<TreeNode*> st1, st2;
        while (root1 || root2 || !st1.empty() || !st2.empty()) {
            while (root1) {
                st1.push(root1);
                root1 = root1->left;
            }
            while (root2) {
                st2.push(root2);
                root2 = root2->left;
            }
            if (st2.empty() || (!st1.empty() && st1.top()->val <= st2.top()->val)) {
                root1 = st1.top(); st1.pop();
                res.push_back(root1->val);
                root1 = root1->right;
            } else {
                root2 = st2.top(); st2.pop();
                res.push_back(root2->val);
                root2 = root2->right;
            }
        }
        return res;
    }
};

Github 同步地址:

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


參考資料:

https://leetcode.com/problems/all-elements-in-two-binary-search-trees/

https://leetcode.com/problems/all-elements-in-two-binary-search-trees/discuss/1719941/C%2B%2B-oror-Best-Explanation-oror-Naive-and-Optimal

https://leetcode.com/problems/all-elements-in-two-binary-search-trees/discuss/1720210/JavaC%2B%2BPython-A-very-very-detailed-EXPLANATION


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