LeetCode(99):恢復二叉搜索樹


Hard!

題目描述:

二叉搜索樹中的兩個節點被錯誤地交換。

請在不改變其結構的情況下,恢復這棵樹。

示例 1:

輸入: [1,3,null,null,2]

   1
  /
 3
  \
   2

輸出: [3,1,null,null,2]

   3
  /
 1
  \
   2

示例 2:

輸入: [3,1,4,null,null,2]

  3
 / \
1   4
   /
  2

輸出: [2,1,4,null,null,3]

  2
 / \
1   4
   /
  3

進階:

  • 使用 O(n) 空間復雜度的解法很容易實現。
  • 你能想出一個只使用常數空間的解決方案嗎?

解題思路:

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

C++解法一:

 1 // O(n) space complexity
 2 class Solution {
 3 public:
 4     void recoverTree(TreeNode *root) {
 5         vector<TreeNode*> list;
 6         vector<int> vals;
 7         inorder(root, list, vals);
 8         sort(vals.begin(), vals.end());
 9         for (int i = 0; i < list.size(); ++i) {
10             list[i]->val = vals[i];
11         }
12     }
13     void inorder(TreeNode *root, vector<TreeNode*> &list, vector<int> &vals) {
14         if (!root) return;
15         inorder(root->left, list, vals);
16         list.push_back(root);
17         vals.push_back(root->val);
18         inorder(root->right, list, vals);
19     }
20 };

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

C++解法二:

 1 // Still O(n) space complexity
 2 class Solution {
 3 public:
 4     TreeNode *pre;
 5     TreeNode *first;
 6     TreeNode *second;
 7     void recoverTree(TreeNode *root) {
 8         pre = NULL;
 9         first = NULL;
10         second = NULL;
11         inorder(root);
12         if (first && second) swap(first->val, second->val);
13     }
14     void inorder(TreeNode *root) {
15         if (!root) return;
16         inorder(root->left);
17         if (!pre) pre = root;
18         else {
19             if (pre->val > root->val) {
20                 if (!first) first = pre;
21                 second = root;
22             }
23             pre = root;
24         }
25         inorder(root->right);
26     }
27 };

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

C++解法三:

 1 // Now O(1) space complexity
 2 class Solution {
 3 public:
 4     void recoverTree(TreeNode *root) {
 5         TreeNode *first = NULL, *second = NULL, *parent = NULL;
 6         TreeNode *cur, *pre;
 7         cur = root;
 8         while (cur) {
 9             if (!cur->left) {
10                 if (parent && parent->val > cur->val) {
11                     if (!first) first = parent;
12                     second = cur;
13                 }
14                 parent = cur;
15                 cur = cur->right;
16             } else {
17                 pre = cur->left;
18                 while (pre->right && pre->right != cur) pre = pre->right;
19                 if (!pre->right) {
20                     pre->right = cur;
21                     cur = cur->left;
22                 } else {
23                     pre->right = NULL;
24                     if (parent->val > cur->val) {
25                         if (!first) first = parent;
26                         second = cur;
27                     }
28                     parent = cur;
29                     cur = cur->right;
30                 }
31             }
32         }
33         if (first && second) swap(first->val, second->val);
34     }
35 };

 


免責聲明!

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



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