二叉树8:寻找错误结点练习题

时间:2023-02-13 21:35:19

题目:一棵二叉树原本是搜索二叉树,但是其中有两个节点调换了位置,使得这棵二叉树不再是搜索二叉树,请找到这两个错误节点并返回他们的值。保证二叉树中结点的值各不相同。给定一棵树的根结点,请返回两个调换了位置的值,其中小的值在前。

思路:对于搜索二叉树,总是与中序遍历关联起来的,根据搜索二叉树的特点,如果一棵树是搜索二叉树,那么当对其进行中序遍历时一定是顺序排列的,本题中说一棵搜索二叉树中的2个结点交换了位置,而由于已知各个结点各不相同因此显然对此时的二叉树进行中序遍历时会出现逆序的结点,即某个结点可能比前一个结点小,这种情况可能出现1次或者2次,例如12345,如果不相邻的元素24交换位置得到14325,那么显然会出现2次减小的情况,4à3和3à2;如果是相邻的2个元素23交换位置得到13245,那么只有1次减小的情况,3à2,因此按照中序遍历的顺序遍历当前的二叉树,每次记录上一个数和当前的数进行比较,如果减小了就是出错的数,记第一次出错时的较大值(当前值的前面一个值)为number1,较小值(当前值)为number2,然后继续向下遍历,如果遇到第2次出错的情况,就用较小的值(当前值)替换number2,表示这才是与number1交换的值,最后显然number1,number2就是交换的2个值,并且number1>number 2,由于要求返回时较小的值在前面,因此返回number2,number1。可以使用递归也可以不使用递归来解决问题。

总的来说第一个出错的数(大)是第一次减小时前面的数;第二个出错的数(小)是最后一次减小时后面的数。

import java.util.*;
/*找出搜索二叉树中交换了位置的2个数:中序遍历找出减小的数,可能有1次减小或者2次较小,
即找出第一次减小时前面的数(大)和最后一次减小时后面的数(小)*/
public class FindErrorNode {
public int[] findError(TreeNode root) {
//特殊输入
if(root==null) return null;
//创建一个数组用来记录结果,Java中数组是对象,可以引用传递
int[] numbers=new int[2];
//求出中序遍历的第一个结点最好不要使用Integer.MIN_VALUE
node=root;
while(node.left!=null){
node=node.left;
}
lastNodeVal=node.val;

//调用递归方法进行中序遍历
this.inOrder(root,numbers);

//返回结果即可,注意需要小的值在前,调整顺序,可以不调整,后面numbers[0]与numbers[1]互换即可
int temp=numbers[0];
numbers[0]=numbers[1];
numbers[1]=temp;
return numbers;
}

//成员变量lastNode,取二叉树的中序遍历第一个结点作为lastNode
TreeNode node=null;
/*此时temp.left==null,说明temp是最左边界上的结点,即是中序遍历的第一个结点
int lastNodeVal;flag==0表示还没有遇到过错误的结点,flag==1表示已经出现过一次错误的结点
int flag=0;*/

//递归方法,对二叉树进行中序遍历并找出减小的2个数
private void inOrder(TreeNode root,int[] numbers){
//递归的边界条件
if(root==null) return;
//①先遍历左子树
this.inOrder(root.left,numbers);
//②遍历中间结点(与前面一个遍历的结点进行比较)
if(root.val<lastNodeVal){
/*此时出现减小的结点
注意:如果只有第一次出现减小的结点时才设置numbers[0],之后再次遇到减小的情况时
不能再更新numbers[0],只能更新numbers[1],因此使用一个标记位flag表示是否是第一次遇到减小的情况从而保证只有第一次出现减小情况时可以更新numbers[0]*/
if(flag==0){
numbers[0]=lastNodeVal;
flag=1;
}
//此时是错误的第二个数,它始终是最后一次减小时的后面的一个数
numbers[1]=root.val;
}
//如果当前值比前一个值大显然要更新lastNodeVal
lastNodeVal=root.val;
//③继续遍历右子树
this.inOrder(root.right,numbers);
}
}