4435 字
约 14 分钟
12
099.恢复二叉搜索树
无标签

099.恢复二叉搜索树

作者:沉默王二
发布时间:2023-11-10T10:03:41.705+0800


本题解同时收录在星球专栏《二哥的 LeetCode 刷题笔记》中,专栏托管在语雀平台上(更方便你沉浸式阅读和做笔记),访问地址和密码:https://t.zsxq.com/6iuzn6I

题意

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

难度

中等

示例

示例 1:

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

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

示例 2:

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

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

分析

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

还记得中序遍历吧?

我们在**094.二叉树的中序遍历 **中曾经提到过,中序遍历是二叉树遍历的一种,它的遍历顺序为:左子树——>根节点——>右子树。

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

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

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

具体怎么实现呢?

还是要依靠递归来帮忙(上一节我们还重点强调过 )。

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

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

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

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

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

/**

*publicclassTreeNode{*intval;*TreeNodeleft;*TreeNoderight;*TreeNode(){}*TreeNode(intval){this.val=val;}*TreeNode(intval,TreeNodeleft,TreeNoderight){this.val=val;this.left=left;this.right=right;}}/classSolution{privateArrayListmidTravel=newArrayList<>();//用来存储中序遍历的结果
publicvoidrecoverTree(TreeNoderoot){if(root==null){//如果根节点为空,则不需要恢复,直接返回return;}middleTravel(root);//执行中序遍历int[]nums=findPosition();//找到被错误交换的两个节点的值checkTravel(root,nums[0],nums[1]);//再次遍历树,修复这两个节点的值}
//中序遍历的辅助方法,用于读取节点的值publicvoidread(TreeNodepos){midTravel.add(pos.val);}
//中序遍历,获得整个二叉树的顺序publicvoidmiddleTravel(TreeNodepos){if(pos==null){return;}middleTravel(pos.left);//遍历左子树read(pos);//读取当前节点middleTravel(pos.right);//遍历右子树}
//找出交换的元素,返回被错误交换的两个节点的值publicint[]findPosition(){intsiz=midTravel.size();//中序遍历结果的大小intpos1=-1,pos2=-1;//初始化两个节点位置for(inti=0;i<siz-1;i++){//如果当前节点值大于下一个节点值,说明找到了一个被交换的节点if(midTravel.get(i+1)<midTravel.get(i)){pos2=i+1;if(pos1==-1){pos1=i;//记录第一个被交换的节点位置}}}returnnewint[]{midTravel.get(pos1),midTravel.get(pos2)};//返回两个被交换节点的值}
//修改元素,修复树结构publicvoidcheckTravel(TreeNodepos,intnum1,intnum2){if(pos==null){return;}//如果当前节点值是被交换的值之一,则将它修复为正确的值if(pos.val==num1){pos.val=num2;}elseif(pos.val==num2){pos.val=num1;}//递归遍历左右子树checkTravel(pos.left,num1,num2);checkTravel(pos.right,num1,num2);}}

一共有三个主要的步骤:

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

来看一下题解效率:

总结

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

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

力扣链接:https://leetcode.cn/problems/recover-binary-search-tree/

一步一个脚印

不积跬步无以至千里,不积小流无以成江海。LeetCode - 100天从算法小白到卷王正式启动了,我们计划周一到周五至少每天更新一篇,周六周日更新一篇,目标 300 道 LeetCode 经典题。

PDF 版本目前只针对**二哥编程星球 的球友开放,如果你也想一起打卡学习算法的话,戳链接 **加入我们吧。

一个人可以走得很快,但一群人才能走得更远。二哥编程星球 (目前已有 3800 多名球友加入)里面的每个球友都非常的友善,除了鼓励你,还会给你提出合理的建议,一起冲!

本题解同时收录在星球专栏《二哥的 LeetCode 刷题笔记》中,专栏托管在语雀平台上(更方便你沉浸式阅读和做笔记),访问地址和密码:https://t.zsxq.com/6iuzn6I

题意

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

难度

中等

示例

示例 1:

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

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

示例 2:

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

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

分析

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

还记得中序遍历吧?

我们在**094.二叉树的中序遍历 **中曾经提到过,中序遍历是二叉树遍历的一种,它的遍历顺序为:左子树——>根节点——>右子树。

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

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

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

具体怎么实现呢?

还是要依靠递归来帮忙(上一节我们还重点强调过 )。

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

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

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

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

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

/**

*publicclassTreeNode{*intval;*TreeNodeleft;*TreeNoderight;*TreeNode(){}*TreeNode(intval){this.val=val;}*TreeNode(intval,TreeNodeleft,TreeNoderight){this.val=val;this.left=left;this.right=right;}}/classSolution{privateArrayListmidTravel=newArrayList<>();//用来存储中序遍历的结果
publicvoidrecoverTree(TreeNoderoot){if(root==null){//如果根节点为空,则不需要恢复,直接返回return;}middleTravel(root);//执行中序遍历int[]nums=findPosition();//找到被错误交换的两个节点的值checkTravel(root,nums[0],nums[1]);//再次遍历树,修复这两个节点的值}
//中序遍历的辅助方法,用于读取节点的值publicvoidread(TreeNodepos){midTravel.add(pos.val);}
//中序遍历,获得整个二叉树的顺序publicvoidmiddleTravel(TreeNodepos){if(pos==null){return;}middleTravel(pos.left);//遍历左子树read(pos);//读取当前节点middleTravel(pos.right);//遍历右子树}
//找出交换的元素,返回被错误交换的两个节点的值publicint[]findPosition(){intsiz=midTravel.size();//中序遍历结果的大小intpos1=-1,pos2=-1;//初始化两个节点位置for(inti=0;i<siz-1;i++){//如果当前节点值大于下一个节点值,说明找到了一个被交换的节点if(midTravel.get(i+1)<midTravel.get(i)){pos2=i+1;if(pos1==-1){pos1=i;//记录第一个被交换的节点位置}}}returnnewint[]{midTravel.get(pos1),midTravel.get(pos2)};//返回两个被交换节点的值}
//修改元素,修复树结构publicvoidcheckTravel(TreeNodepos,intnum1,intnum2){if(pos==null){return;}//如果当前节点值是被交换的值之一,则将它修复为正确的值if(pos.val==num1){pos.val=num2;}elseif(pos.val==num2){pos.val=num1;}//递归遍历左右子树checkTravel(pos.left,num1,num2);checkTravel(pos.right,num1,num2);}}

一共有三个主要的步骤:

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

来看一下题解效率:

总结

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

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

力扣链接:https://leetcode.cn/problems/recover-binary-search-tree/

一步一个脚印

不积跬步无以至千里,不积小流无以成江海。LeetCode - 100天从算法小白到卷王正式启动了,我们计划周一到周五至少每天更新一篇,周六周日更新一篇,目标 300 道 LeetCode 经典题。

PDF 版本目前只针对**二哥编程星球 的球友开放,如果你也想一起打卡学习算法的话,戳链接 **加入我们吧。

一个人可以走得很快,但一群人才能走得更远。二哥编程星球 (目前已有 3800 多名球友加入)里面的每个球友都非常的友善,除了鼓励你,还会给你提出合理的建议,一起冲!

本题解同时收录在星球专栏《二哥的 LeetCode 刷题笔记》中,专栏托管在语雀平台上(更方便你沉浸式阅读和做笔记),访问地址和密码:https://t.zsxq.com/6iuzn6I

题意

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

难度

中等

示例

示例 1:

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

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

示例 2:

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

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

分析

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

还记得中序遍历吧?

我们在**094.二叉树的中序遍历 **中曾经提到过,中序遍历是二叉树遍历的一种,它的遍历顺序为:左子树——>根节点——>右子树。

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

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

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

具体怎么实现呢?

还是要依靠递归来帮忙(上一节我们还重点强调过 )。

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

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

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

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

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

/**

*publicclassTreeNode{*intval;*TreeNodeleft;*TreeNoderight;*TreeNode(){}*TreeNode(intval){this.val=val;}*TreeNode(intval,TreeNodeleft,TreeNoderight){this.val=val;this.left=left;this.right=right;}}/classSolution{privateArrayListmidTravel=newArrayList<>();//用来存储中序遍历的结果
publicvoidrecoverTree(TreeNoderoot){if(root==null){//如果根节点为空,则不需要恢复,直接返回return;}middleTravel(root);//执行中序遍历int[]nums=findPosition();//找到被错误交换的两个节点的值checkTravel(root,nums[0],nums[1]);//再次遍历树,修复这两个节点的值}
//中序遍历的辅助方法,用于读取节点的值publicvoidread(TreeNodepos){midTravel.add(pos.val);}
//中序遍历,获得整个二叉树的顺序publicvoidmiddleTravel(TreeNodepos){if(pos==null){return;}middleTravel(pos.left);//遍历左子树read(pos);//读取当前节点middleTravel(pos.right);//遍历右子树}
//找出交换的元素,返回被错误交换的两个节点的值publicint[]findPosition(){intsiz=midTravel.size();//中序遍历结果的大小intpos1=-1,pos2=-1;//初始化两个节点位置for(inti=0;i<siz-1;i++){//如果当前节点值大于下一个节点值,说明找到了一个被交换的节点if(midTravel.get(i+1)<midTravel.get(i)){pos2=i+1;if(pos1==-1){pos1=i;//记录第一个被交换的节点位置}}}returnnewint[]{midTravel.get(pos1),midTravel.get(pos2)};//返回两个被交换节点的值}
//修改元素,修复树结构publicvoidcheckTravel(TreeNodepos,intnum1,intnum2){if(pos==null){return;}//如果当前节点值是被交换的值之一,则将它修复为正确的值if(pos.val==num1){pos.val=num2;}elseif(pos.val==num2){pos.val=num1;}//递归遍历左右子树checkTravel(pos.left,num1,num2);checkTravel(pos.right,num1,num2);}}

一共有三个主要的步骤:

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

来看一下题解效率:

总结

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

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

力扣链接:https://leetcode.cn/problems/recover-binary-search-tree/

一步一个脚印

不积跬步无以至千里,不积小流无以成江海。LeetCode - 100天从算法小白到卷王正式启动了,我们计划周一到周五至少每天更新一篇,周六周日更新一篇,目标 300 道 LeetCode 经典题。

PDF 版本目前只针对**二哥编程星球 的球友开放,如果你也想一起打卡学习算法的话,戳链接 **加入我们吧。

一个人可以走得很快,但一群人才能走得更远。二哥编程星球 (目前已有 3800 多名球友加入)里面的每个球友都非常的友善,除了鼓励你,还会给你提出合理的建议,一起冲!

            知识星球
            
    
    
        
        扫码加入星球
        查看更多优质内容
    
    https://wx.zsxq.com/mweb/views/joingroup/join_group.html?group_id=15522885221412
099.恢复二叉搜索树
https://clxhxhhr.top/posts/2553/
作者
clxstart
发布于
2026-09-19
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。
☰ 文章目录