恢复两节点被交换的二叉搜索树-恢复二叉搜索树-99

题意

给你二叉搜索树的根节点 root ,该树中 恰好 有两个节点的值被错误地交换。_请在不改变其结构的情况下,恢复这棵树_ 。

难度

中等

示例

示例 1:

输入:root=[1,3,null,null,2]

输出:[3,1,null,null,2]

解释:3不能是1的左孩子,因为3>1。交换1和3使二叉搜索树有效。

示例 2:

输入:root=[3,1,4,null,null,2]

输出:[2,1,4,null,null,3]

解释:2不能在3的右子树中,因为2<3。交换2和3使二叉搜索树有效。

分析

二叉搜索树一个重要的特点就是:中序遍历必然是一个单调递增的序列。

还记得中序遍历吧?

中序遍历是二叉树遍历的一种,它的遍历顺序为:左子树——>根节点——>右子树。

那为什么中序遍历必然是一个单调递增的序列呢?

因为中序遍历是先遍历左子树的,而左子树的值都比当前节点的小,然后是当前节点,再然后遍历右子树,而右子树的值又比当前节点的大。

很显然,如果有两个节点的位置恰好被错误的交换,那么中序遍历的时候,单调递增的特征就会被破坏掉,而此时,我们只需要找出这两个错误调换的节点,就能恢复二叉搜索树了。

具体怎么实现呢?

还是要依靠递归来帮忙

我们首先来中序遍历整个二叉搜索树。

接着从序列中找到nums[i] > nums[i + 1]的地方,因为正常的序列为递增序列,出现反常的地方,必然是交换的地方。

这样我们便可以找出交换过的节点,下一步,交换这个两个节点就能恢复原始的二叉搜索树了。

这次还是使用一次遍历,找到错误交换的两个节点,直接交换其位置即可。

一共三次遍历,第一次是中序遍历求序列,第二次是找调换位置,第三次是遍历二叉搜索树修改对应节点。

/**

*Definitionforabinarytreenode.

*public class TreeNode{

*int val;

*TreeNode left;

*TreeNode right;

*TreeNode(){}

*TreeNode(int val){

this.val=val;

}

*TreeNode(int val,TreeNode left,TreeNode right){

*this.val=val;

*this.left=left;

*this.right=right;

*}

*}

*/

class Solution{

private  ArrayList<Integer>  midTravel  =  new ArrayList<>();//用来存储中序遍历的结果

 

public void  recoverTree(TreeNode  root){

if(root==null){//如果根节点为空,则不需要恢复,直接返回

return;

}

middleTravel(root);//执行中序遍历

int[]  nums = findPosition();//找到被错误交换的两个节点的值

checkTravel(root,nums[0],nums[1]);//再次遍历树,修复这两个节点的值

}



//中序遍历的辅助方法,用于读取节点的值

public  void  read(TreeNode  pos){

midTravel.add(pos.val);

}



//中序遍历,获得整个二叉树的顺序

public  void  middleTravel(TreeNode  pos){

if(pos==null){

return;

}

middleTravel(pos.left);//遍历左子树

read(pos);//读取当前节点

middleTravel(pos.right);//遍历右子树

}



//找出交换的元素,返回被错误交换的两个节点的值

public int[]  findPosition(){

int  siz = midTravel.size();//中序遍历结果的大小

int  pos1 = -1, pos2 = -1;//初始化两个节点位置

for(int i=0;i<siz-1;i++){

//如果当前节点值大于下一个节点值,说明找到了一个被交换的节点

if(midTravel.get(i+1)<midTravel.get(i)){

pos2=i+1;

if(pos1==-1){

pos1=i;//记录第一个被交换的节点位置

}

}

}

return  new  int[] {midTravel.get(pos1),midTravel.get(pos2)};//返回两个被交换节点的值

}



//修改元素,修复树结构

public  void  checkTravel(TreeNode  pos,int  num1,int  num2){

if(pos==null){

return;

}

//如果当前节点值是被交换的值之一,则将它修复为正确的值

if(pos.val==num1){

pos.val=num2;

}else if(pos.val==num2){

pos.val=num1;

}

//递归遍历左右子树

checkTravel(pos.left,num1,num2);

checkTravel(pos.right,num1,num2);

}

}

一共有三个主要的步骤:

  1. 中序遍历二叉搜索树,得到一个升序的数列。
  2. 使用中序遍历找到二叉搜索树中顺序错误的两个节点。在正常情况下,中序遍历二叉搜索树应该得到一个升序的数列。如果有两个数的顺序被交换了,那么就会出现两处顺序错误:第一处是较大的数与它后面的一个较小的数交换了位置,第二处是较小的数与它后面的一个较大的数交换了位置。
  3. 再次遍历树,修复这两个节点的值。遍历时检查每个节点的值,如果与被交换的两个值中的任何一个匹配,就将其替换为另一个。这样就能保证这两个节点的值被交换回来,从而恢复二叉搜索树的性质。

来看一下题解效率:

总结

AVL 树是二叉搜索树(BST)的一种,所以它继承了 BST 的所有特性。在 AVL 树中,对于任意节点,其左子树中的所有值都小于该节点的值,其右子树中的所有值都大于该节点的值。

本题并不要求我们处理或者维持 AVL 树的平衡,但在处理 BST 时理解其性质和可能的操作(如旋转)对于解决相关问题是有帮助的。

力扣链接:球极客挚爱的技术成长平台

一步一个脚印

不积跬步无以至千里,不积小流无以成江海。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值