C++實(shí)現(xiàn)LeetCode(99.復(fù)原二叉搜索樹)
[LeetCode] 99. Recover Binary Search Tree 復(fù)原二叉搜索樹
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
\
2Output: [3,1,null,null,2]
3
/
1
\
2
Example 2:
Input: [3,1,4,null,null,2]
3
/ \
1 4
/
2Output: [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?
這道題要求我們復(fù)原一個(gè)二叉搜索樹,說是其中有兩個(gè)的順序被調(diào)換了,題目要求上說 O(n) 的解法很直觀,這種解法需要用到遞歸,用中序遍歷樹,并將所有節(jié)點(diǎn)存到一個(gè)一維向量中,把所有節(jié)點(diǎn)值存到另一個(gè)一維向量中,然后對存節(jié)點(diǎn)值的一維向量排序,在將排好的數(shù)組按順序賦給節(jié)點(diǎn)。這種最一般的解法可針對任意個(gè)數(shù)目的節(jié)點(diǎn)錯(cuò)亂的情況,這里先貼上此種解法:
解法一:
// 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);
}
};
然后博主上網(wǎng)搜了許多其他解法,看到另一種是用雙指針來代替一維向量的,但是這種方法用到了遞歸,也不是 O(1) 空間復(fù)雜度的解法,這里需要三個(gè)指針,first,second 分別表示第一個(gè)和第二個(gè)錯(cuò)亂位置的節(jié)點(diǎn),pre 指向當(dāng)前節(jié)點(diǎn)的中序遍歷的前一個(gè)節(jié)點(diǎn)。這里用傳統(tǒng)的中序遍歷遞歸來做,不過在應(yīng)該輸出節(jié)點(diǎn)值的地方,換成了判斷 pre 和當(dāng)前節(jié)點(diǎn)值的大小,如果 pre 的大,若 first 為空,則將 first 指向 pre 指的節(jié)點(diǎn),把 second 指向當(dāng)前節(jié)點(diǎn)。這樣中序遍歷完整個(gè)樹,若 first 和 second 都存在,則交換它們的節(jié)點(diǎn)值即可。這個(gè)算法的空間復(fù)雜度仍為 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);
}
};
我們其實(shí)也可以使用迭代的寫法,因?yàn)橹行虮闅v Binary Tree Inorder Traversal 也可以借助棧來實(shí)現(xiàn),原理還是跟前面的相同,記錄前一個(gè)結(jié)點(diǎn),并和當(dāng)前結(jié)點(diǎn)相比,如果前一個(gè)結(jié)點(diǎn)值大,那么更新 first 和 second,最后交換 first 和 second 的結(jié)點(diǎn)值即可,參見代碼如下:
解法三:
// 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);
}
};
這道題的真正符合要求的解法應(yīng)該用的 Morris 遍歷,這是一種非遞歸且不使用棧,空間復(fù)雜度為 O(1) 的遍歷方法,可參見博主之前的博客 Binary Tree Inorder Traversal,在其基礎(chǔ)上做些修改,加入 first, second 和 parent 指針,來比較當(dāng)前節(jié)點(diǎn)值和中序遍歷的前一節(jié)點(diǎn)值的大小,跟上面遞歸算法的思路相似,代碼如下:
解法四:
// 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);
}
};
到此這篇關(guān)于C++實(shí)現(xiàn)LeetCode(99.復(fù)原二叉搜索樹)的文章就介紹到這了,更多相關(guān)C++實(shí)現(xiàn)復(fù)原二叉搜索樹內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C++數(shù)據(jù)結(jié)構(gòu)二叉搜索樹的實(shí)現(xiàn)應(yīng)用與分析
- C++實(shí)現(xiàn)LeetCode(173.二叉搜索樹迭代器)
- C++實(shí)現(xiàn)LeetCode(109.將有序鏈表轉(zhuǎn)為二叉搜索樹)
- C++實(shí)現(xiàn)LeetCode(108.將有序數(shù)組轉(zhuǎn)為二叉搜索樹)
- C++實(shí)現(xiàn)LeetCode(98.驗(yàn)證二叉搜索樹)
- C++實(shí)現(xiàn)LeetCode(96.獨(dú)一無二的二叉搜索樹)
- C++實(shí)現(xiàn)LeetCode(95.獨(dú)一無二的二叉搜索樹之二)
- C++ 二叉搜索樹(BST)的實(shí)現(xiàn)方法
- C++ 超詳細(xì)快速掌握二叉搜索樹
相關(guān)文章
VSCode 使用 Code Runner 插件無法編譯運(yùn)行文件名帶空格的文件問題
這篇文章主要介紹了VSCode 使用 Code Runner 插件無法編譯運(yùn)行文件名帶空格的文件問題,本文通過圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-07-07
C語言分別實(shí)現(xiàn)棧和隊(duì)列詳解流程
棧和隊(duì)列,嚴(yán)格意義上來說,也屬于線性表,因?yàn)樗鼈円捕加糜诖鎯?chǔ)邏輯關(guān)系為 "一對一" 的數(shù)據(jù),但由于它們比較特殊,因此將其單獨(dú)作為一章,做重點(diǎn)講解2022-04-04
用c語言實(shí)現(xiàn)HUP信號(hào)重啟進(jìn)程的方法
本篇文章是對使用c語言實(shí)現(xiàn)HUP信號(hào)重啟進(jìn)程的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05

