[LeetCode] 99. Recover Binary Search Tree 復原二叉搜索樹


 

Two elements of a binary search tree (BST) are swapped by mistake.

Recover the tree without changing its structure.

Example 1:

Input: [1,3,null,null,2]

   1
  /
 3
  \
   2

Output: [3,1,null,null,2]

   3
  /
 1
  \
   2

Example 2:

Input: [3,1,4,null,null,2]

  3
 / \
1   4
   /
  2

Output: [2,1,4,null,null,3]

  2
 / \
1   4
   /
  3

Follow up:

  • A solution using O(n) space is pretty straight forward.
  • Could you devise a constant space solution?

 

這道題要求我們復原一個二叉搜索樹,說是其中有兩個的順序被調換了,題目要求上說 O(n) 的解法很直觀,這種解法需要用到遞歸,用中序遍歷樹,並將所有節點存到一個一維向量中,把所有節點值存到另一個一維向量中,然后對存節點值的一維向量排序,在將排好的數組按順序賦給節點。這種最一般的解法可針對任意個數目的節點錯亂的情況,這里先貼上此種解法:

 

解法一:

// O(n) space complexity
class Solution {
public:
    void recoverTree(TreeNode* root) {
        vector<TreeNode*> list;
        vector<int> vals;
        inorder(root, list, vals);
        sort(vals.begin(), vals.end());
        for (int i = 0; i < list.size(); ++i) {
            list[i]->val = vals[i];
        }
    }
    void inorder(TreeNode* root, vector<TreeNode*>& list, vector<int>& vals) {
        if (!root) return;
        inorder(root->left, list, vals);
        list.push_back(root);
        vals.push_back(root->val);
        inorder(root->right, list, vals);
    }
};

 

然后博主上網搜了許多其他解法,看到另一種是用雙指針來代替一維向量的,但是這種方法用到了遞歸,也不是 O(1) 空間復雜度的解法,這里需要三個指針,first,second 分別表示第一個和第二個錯亂位置的節點,pre 指向當前節點的中序遍歷的前一個節點。這里用傳統的中序遍歷遞歸來做,不過再應該輸出節點值的地方,換成了判斷 pre 和當前節點值的大小,如果 pre 的大,若 first 為空,則將 first 指向 pre 指的節點,把 second 指向當前節點。這樣中序遍歷完整個樹,若 first 和 second 都存在,則交換它們的節點值即可。這個算法的空間復雜度仍為 O(n),n為樹的高度,參見代碼如下:

 

解法二:

// Still O(n) space complexity
class Solution {
public:
    TreeNode *pre = NULL, *first = NULL, *second = NULL;
    void recoverTree(TreeNode* root) {
        inorder(root);
        swap(first->val, second->val);
    }
    void inorder(TreeNode* root) {
        if (!root) return;
        inorder(root->left);
        if (!pre) pre = root;
        else {
            if (pre->val > root->val) {
                if (!first) first = pre;
                second = root;
            }
            pre = root;
        }
        inorder(root->right);
    }
};

 

我們其實也可以使用迭代的寫法,因為中序遍歷 Binary Tree Inorder Traversal 也可以借助棧來實現,原理還是跟前面的相同,記錄前一個結點,並和當前結點相比,如果前一個結點值大,那么更新 first 和 second,最后交換 first 和 second 的結點值即可,參見代碼如下:

 

解法三:

// Always O(n) space complexity
class Solution {
public:
    void recoverTree(TreeNode* root) {
        TreeNode *pre = NULL, *first = NULL, *second = NULL, *p = root;
        stack<TreeNode*> st;
        while (p || !st.empty()) {
            while (p) {
                st.push(p);
                p = p->left;
            }
            p = st.top(); st.pop();
            if (pre) {
                if (pre->val > p->val) {
                    if (!first) first = pre;
                    second = p;
                }
            }
            pre = p;
            p = p->right;
        }
        swap(first->val, second->val);
    }
};

 

這道題的真正符合要求的解法應該用的 Morris 遍歷,這是一種非遞歸且不使用棧,空間復雜度為 O(1) 的遍歷方法,可參見博主之前的博客 Binary Tree Inorder Traversal,在其基礎上做些修改,加入 first, second 和 parent 指針,來比較當前節點值和中序遍歷的前一節點值的大小,跟上面遞歸算法的思路相似,代碼如下:

 

解法四:

// Now O(1) space complexity
class Solution {
public:
    void recoverTree(TreeNode* root) {
        TreeNode *first = nullptr, *second = nullptr, *cur = root, *pre = nullptr ;
        while (cur) {
            if (cur->left){
                TreeNode *p = cur->left;
                while (p->right && p->right != cur) p = p->right;
                if (!p->right) {
                    p->right = cur;
                    cur = cur->left;
                    continue;
                } else {
                    p->right = NULL;
                }  
            }
            if (pre && cur->val < pre->val){
              if (!first) first = pre;
              second = cur;
            }
            pre = cur;
            cur = cur->right;
        }
        swap(first->val, second->val);
    }
};

 

Github 同步地址:

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

 

類似題目:

Binary Tree Inorder Traversal

 

參考資料:

https://leetcode.com/problems/recover-binary-search-tree/

https://leetcode.com/problems/recover-binary-search-tree/discuss/32607/Beat-99-Fast-Java-Solution-O(h)-Space-with-Explanation 

https://leetcode.com/problems/recover-binary-search-tree/discuss/32535/No-Fancy-Algorithm-just-Simple-and-Powerful-In-Order-Traversal

https://leetcode.com/problems/recover-binary-search-tree/discuss/32559/Detail-Explain-about-How-Morris-Traversal-Finds-two-Incorrect-Pointer

 

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


免責聲明!

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



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