

思路:
中序遍历BST,依次访问的节点值是递增的,错误的BST会破坏递增性,从而能定位出错误。
我发现,错误有两种:

特别注意:如果是错误1,交换第一对的第一个元素与第二对的第二个元素
代码:
class Solution {
public:
TreeNode* prev=NULL,*p1=NULL,*p2=NULL;
void recoverTree(TreeNode* root)
{
if (root == NULL)
return;
//这里prev初始默认为最小值
prev = new TreeNode(INT_MIN);
travle(root);
swap(p1->val, p2->val);
}
void travle(TreeNode* root)
{
if (!root) return;
travle(root->left);
if (prev->val > root->val && p1 == NULL)// 当前是第一对错误
p1 = prev; // 记录第一个错误点
if (prev->val > root->val && p1 != NULL)//当前是第二对错误
p2 = root; //记录第二个错误点
//一开始把prev设置成最小值是因为,通过递归最开始处理的是左子树最左下角的叶子节点,
//我们一开始要让该节点赋值给prev,不能让prev满足上面两个条件--取int的最小值
prev = root;// 更新 perv
travle(root->right);
}
};
class Solution {
public:
TreeNode* prev=NULL,*p1=NULL,*p2=NULL;
void recoverTree(TreeNode* root)
{
if (root == NULL)
return;
travle(root);
swap(p1->val, p2->val);
}
void travle(TreeNode* root)
{
if (!root) return;
//说明当前节点左子树最下面的叶子节点
if (root->left == NULL && prev == NULL) prev = root;
travle(root->left);
if (prev->val > root->val && p1 == NULL)// 当前是第一对错误
p1 = prev; // 记录第一个错误点
if (prev->val > root->val && p1 != NULL)//当前是第二对错误
p2 = root; //记录第二个错误点
prev = root;// 更新 perv
travle(root->right);
}
};
if (root->left == NULL && prev == NULL) prev = root; 这里不能直接return 返回class Solution {
public:
TreeNode* prev=NULL,*p1=NULL,*p2=NULL;
int num = 0;// 记录出现的逆序对数
void recoverTree(TreeNode* root)
{
if (root == NULL)
return;
travle(root);
swap(p1->val, p2->val);
}
void travle(TreeNode* root)
{
if (!root) return;
if (num == 2) return;
//这里不能直接return,因为当前节点
if (root->left == NULL && prev == NULL) prev = root;
travle(root->left);
if (prev->val > root->val)
{
if (!p1) p1 = prev;// 出现第二对时, p1就不再赋值
p2 = root;
num++;
}
prev = root;// 更新 perv
travle(root->right);
}
};

class Solution {
vector<TreeNode*> ret;
public:
void recoverTree(TreeNode* root)
{
if (root == NULL)
return;
dfs(root);
int p1=INT_MIN, p2;//记录需要交换的两个位置下标
//注意:i的终止范围是ret.size()-1,因为这里比较中存在i+1
for (int i = 0; i < ret.size()-1; i++)
{
if (ret[i]->val > ret[i + 1]->val)
{
if (p1 == INT_MIN)
{
p1 = i;
}
p2 = i + 1;
}
}
swap(ret[p1]->val, ret[p2]->val);
}
void dfs(TreeNode* root)
{
if (!root) return;
dfs(root->left);
ret.push_back(root);
dfs(root->right);
}
};

回想一下中序遍历的递归版本是
dfs(root.left)
打印节点 root
dfs(root.right)

Morris遍历算法的步骤如下:
Morris代码框架:
while(cur){
if(cur->left == NULL){// 左子树为空时,直接比较,然后进入右子树
/*************
/* 进行你的操作
*************/
pre = cur;
cur = cur->right;
}else{// 进入左子树
/*************
/* 找cur的前驱结点,找到后分两种情况
/* 1、cur的左子结点没有右子结点,那cur的左子结点就是前驱
/* 2、cur的左子结点有右子结点,就一路向右下,走到底就是cur的前驱
*************/
TreeNode* preceesor = cur->left;
while(preceesor->right && preceesor->right != cur){
preceesor = preceesor->right;
}
// 前驱已经指向自己了,直接比较,然后进入右子树
if(preceesor->right == cur){
/*************
/* 进行你的操作
*************/
preceesor->right = NULL; // 断开连接,恢复原树
pre = cur;
cur = cur->right;
}else{ // 前驱还没有指向自己,说明左边还没有遍历,将前驱的右指针指向自己,后进入前驱
preceesor->right = cur;
cur = cur->left;
}
}
}代码:
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;
}
};