[LeetCode] Recover Binary Search Tree 復原二叉搜尋樹
Two elements of a binary search tree (BST) are swapped by mistake.
Recover the tree without changing its structure.
Note:
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; TreeNode *first; TreeNode *second; void recoverTree(TreeNode *root) { pre = NULL; first = NULL; second = NULL; inorder(root); if (first && second) 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); } };
這道題的真正符合要求的解法應該用的Morris遍歷,這是一種非遞迴且不使用棧,空間複雜度為O(1)的遍歷方法,可參見我之前的部落格Binary Tree Inorder Traversal 二叉樹的中序遍歷,在其基礎上做些修改,加入first, second和parent指標,來比較當前節點值和中序遍歷的前一節點值的大小,跟上面遞迴演算法的思路相似,程式碼如下:
// Now O(1) space complexity class Solution { public: void recoverTree(TreeNode *root) { TreeNode *first = NULL, *second = NULL, *parent = NULL; TreeNode *cur, *pre; cur = root; while (cur) { if (!cur->left) { if (parent && parent->val > cur->val) { if (!first) first = parent; second = cur; } parent = cur; cur = cur->right; } else { pre = cur->left; while (pre->right && pre->right != cur) pre = pre->right; if (!pre->right) { pre->right = cur; cur = cur->left; } else { pre->right = NULL; if (parent->val > cur->val) { if (!first) first = parent; second = cur; } parent = cur; cur = cur->right; } } } if (first && second) swap(first->val, second->val); } };