题目描述
99.恢复二叉搜索树
二叉搜索树中的两个节点被错误地交换。
请在不改变其结构的情况下,恢复这棵树。
示例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) 空间复杂度的解法很容易实现。
- 你能想出一个只使用常数空间的解决方案吗?
解题思路
1.递归
最初思路:因为二叉搜索树是有序的,因此对于任意一种树的结构,每个值对应的结点是唯一的,因此可以根据这个性质,来排序每个结点。
又因为对于二叉搜索树,对其进行中序遍历时,得到的序列必然是升序的,因此可以采用中序遍历+交换结点 val 的思路来解决这道题。
但是这种递归仅能解决相邻之间元素错乱的情况,对于示例2中2,3位置错误就无法交换
代码如下
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
void recoverTree(TreeNode* root) {
if(root->left!=NULL)
{
recoverTree(root->left);
if(root->val < root->left->val)
{
int temp = root->val;
root->val=root->left->val;
root->left->val=temp;
}
}
if(root->right!=NULL)
{
if(root->val < root->right->val)
{
int temp = root->val;
root->val=root->right->val;
root->right->val=temp;
}
recoverTree(root->right);
}
}
};
执行结果
2.Morris遍历
代码如下
class Solution {
public:
// s1 存小索引那个结点,s2存大索引那个结点,pre存前驱结点
TreeNode *s1 = NULL, *s2 = NULL, *pre = NULL;
void recoverTree(TreeNode* root) {
TreeNode* cur = root;
while(cur != NULL){
if(cur->left != NULL){ // 进入左子树
// 找到cur的前驱结点,分两种情况
// 1、cur的左子结点没有右子结点,那cur的左子结点就是前驱
// 2、cur的左子结点有右子结点,就一路向右下,走到底就是cur的前驱
TreeNode* predecessor = cur->left;
while(predecessor->right != NULL && predecessor->right != cur){
predecessor = predecessor->right;
}
// 前驱还没有指向自己,说明左边还没有遍历,将前驱的右指针指向自己,后进入前驱
if(predecessor->right == NULL){
predecessor->right = cur;
cur = cur->left;
}else{
// 前驱已经指向自己了,直接比较是否有逆序对,然后进入右子树
if(pre != NULL && cur->val < pre->val){
if(s1 == NULL) s1 = pre;
s2 = cur;
}
pre = cur;
predecessor->right = NULL;
cur = cur->right;
}
}else{ // 左子树为空时,检查是否有逆序对,然后进入右子树
if(pre != NULL && cur->val < pre->val){
if(s1 == NULL) s1 = pre;
s2 = cur;
}
pre = cur;
cur = cur->right;
}
}
// 进行交换
int t = s1->val;
s1->val = s2->val;
s2->val = t;
return;
}
};