思路一(不满足题目要求的做法):二叉搜索树有一个特性,就是他的左孩子的节点的值比根节点的值小,右孩子的节点的值比根节点的大,所以我们在中序遍历二叉搜索数的时候会得到一个升序的序列。题目说节点被交换了。不管他交换了多少个节点,我们先把其中序遍历的结果储存到一个数据容器里面去,然后对这个数据容器的数字从小到大排序。排序后就中序遍历原来的二叉搜索树,然后把数据容器的每一个数字赋值到原来的树中。既然要这个数据容器的数据是有序的状态,那么直接用优先队列就好啦。
class Solution {
public:
//优先队列储存二叉搜索树中序遍历的结果
priority_queue <int , vector<int>, greater<int> >temp;
void recoverTree(TreeNode *root) {
if(root == nullptr)
return ;
dfs(root);//中序遍历树
modify(root);
return ;
}
void dfs(TreeNode * root)
{
if(root)
{
dfs(root -> left);
temp.push (root->val);//把节点的值放到优先队列,队列会对值自动排序
dfs(root -> right);
}
}
void modify(TreeNode * root)//修改原来的二叉树。
{
if(root)
{
modify(root -> left);//修改左子树
root -> val = temp.top ();//把值黏贴回来正确的位置
temp.pop();//准备黏贴下一个数值
modify(root -> right);//修改右子树
}
}
};
思路二:
可以发现一正常二叉搜索树中序遍历的结果是升序的。那么如果交换了节点,则中序遍历的结果就不是升序 的,则一定存在逆序对!比如下面这个交换了节点的图
中序遍历的结果是7 5 6 4 ,是4 和7的位置交换错了。
正常的顺序是4 5 6 7 ,可看到只要找到中序遍历结果的 逆序对 的位置,那就可以确认交换节点的位置。
可以用一个指针first指向第一逆序对的位置,second指向第二个逆序对的位置。
pre指向中序遍历的当前节点的前驱节点。
如果发现当前节点和前驱节点是pre-val> root->val(root是当前节点),那么就可以确认一个逆序对的出现位置。
最后交换first,和second指针所指的值即可。
class Solution {
public:
//first指向第一个逆序对的位置,
//second指向第二个逆序对的位置
//pre指向中续遍历的前驱节点
TreeNode * first , *second, *pre;
Solution()//构造初始化
{
first = nullptr;
second = nullptr;
pre = nullptr;
}
void swap(int & a, int & b)
{
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
void recoverTree(TreeNode *root) {
if(root == nullptr)
return ;
dfs(root);
//cout<<"check p : "<<bool(first)<<" "<<bool(second)<<endl;
if(first && second )
swap(first->val, second->val);
return ;
}
void dfs(TreeNode * root)
{
if(root )
{
dfs(root -> left);//中序遍历
if(!pre)//前驱节点的起始
pre = root;
else
{
if( pre && pre->val > root->val )//找到逆序对
{
if(first == nullptr)//记录第一个逆序对出现的位置
first = pre;
second = root;//记得直接复制啊,当树只有两个节点的时候
//且节点位置交换了之后这个第二个交换的节点位置就是当前节点了
//比如【0 1 # # #】这二叉树
}
}
pre = root;//前驱节点变为当前节点
dfs(root -> right);//遍历右子树
}
}
};