首页 > 代码库 > 二叉树进阶之寻找一棵二叉树中的最大二叉搜索子树
二叉树进阶之寻找一棵二叉树中的最大二叉搜索子树
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6618915.html
(规律:在二叉树中寻找某性质的,都应该以递归思维:从根结点开始递归左右,一直到底,由底向上返回的信息来判断当前结点、求当前结点。即:二叉树的题目,从下往上想,递归的返回过程就是从下往上由叶到根建立二叉树的过程,在此过程中对每一步的“根”结点作性质判断,返回到根时即是整棵树的性质判断了)
从一棵树中寻找结点数最多的二叉搜索子树,并返回这棵子树的头结点。
从题目我们知道以下要求:
1:子树是二叉搜索树,即:左儿子小于父节点,右儿子大于父节点,并且这个性质在整棵树的任一子树都成立。——从任一子树成立,可以得知:一棵二叉搜索子树的本身,是从它的子树也是二叉搜索树递推上来的。因此,我们在递归时,从叶到根逐步建立二叉树的过程中可以每一步都知道当前“根”的子树是否满足二叉搜索树性质,是则返回这个“根”,否则返回一个儿结点。对建立二叉树的过程中每一步的当前“根”,我们都假设左右子树是二叉搜索树,那么递归左子树时返回的就是左儿子,递归右子树时返回的就是右儿子。因此我们对当前结点作以下判断:左子树最大值<当前结点值<右子树最小值 && 左子树中的二叉搜索树根是左儿子 && 右子树中的二叉搜索树根是是右儿子,则当前结点为“根”的子树也是二叉搜索树,把当前结点返回递归上层给他的父节点作判断使用,否则返回一个儿结点(只要父节点发现递归子树时返回的不是自己的儿子,即可说明以父节点为根的子树不是二叉搜索树)。
2:二叉搜索子树的结点数最大。一个结点为根的子树中找结点数最多的二叉搜索子树,有两种情况:
1)当前结点左子树是二叉搜索树,当前结点右子树是二叉搜索树,并满足 left_max<curr<right_min,那么以当前结点为根的树也是二叉搜索树,把curr往上传递;
2)如果不满足第一点,则当前结点为根的结点数最多二叉搜索子树是它的左右子树中结点数最大的那个二叉搜索子树,返回左右子树中结点数最多的二叉搜索子树的根结点向上传递。
3:极限情况下,整棵树中没有一棵二叉搜索子树,那么递归到叶子结点的儿结点null时,返回null。然后在逐层向上返回时,因为没有一棵二叉搜索子树,所以null被一直向上传递返回。最终得到结果就是Null.也就是说:只要有一棵二叉搜索子树,那么它的“根”结点就会被往上传递,作为它的众多祖先们(包括父亲)的子树中的候选最大二叉搜索子树直到被抛弃(在某一层时被另一子树传上来的最大二叉搜索子树取代)。
采用后序遍历的方式来遍历二叉树(后序遍历二叉树的过程相当于从下往上逐层重建二叉树的过程,先遍历左右儿子得到所需信息来判断当前结点,特别适合于这种由“小树”求“大树”的题目),每层需要采集以下信息:
1:左子树中的最大值,左子树中最小值、左子树的最大二叉搜索子树的根结点、左子树中最大二叉搜索子树的结点数目(从下往上创建的二叉搜索子树,每处理一个结点若当前结点为根的树是二叉搜索树,则结点数目=左子树结点数+右子树结点数+1.而左右子树结点数又是从下往上传来的,从最底层null传来的是0,每层往上=左子树结点数+右子树结点数+1)【递归用到的计数,都是从递归边界开始的!所以写递归时一定要先写好递归边界的返回情况】
2:右子树的同样4样信息。
3:多信息采集的递归,我们采取“结点作返回值,其他信息用数组”的方法。
public TreeNode getMax(TreeNode root) { if(root==null){ return null; } //用数组记录子树中信息:最大值、最小值、子树中最大二叉搜索树的结点数 int[] childtree_info=new int[3]; return postfind(root,childtree_info); } public TreeNode postfind(TreeNode curr,int[] childtree_info){ //递归边界:null结点。递归用到的计数,都是从递归边界开始的!所以写递归时一定要先写好递归边界的返回情况 if(curr==null){ childtree_info[0]=Integer.MIN_VALUE;//Null结点,子树最大值不存在,所以令为很小值 childtree_info[1]=Integer.MAX_VALUE;//Null结点,子树最小值不存在,所以令为很大值 childtree_info[2]=0;//Null结点的最大二叉搜索子树结点数为0 return curr; } //递归左右子树,获取4个信息 TreeNode left_maxBST=postfind(curr.left,childtree_info); int left_maxBST_max=childtree_info[0]; int left_maxBST_min = childtree_info[1]; int left_maxBST_nodesnum = childtree_info[2]; TreeNode right_maxBST=postfind(curr.right,childtree_info); int right_maxBST_max = childtree_info[0]; int right_maxBST_min = childtree_info[1]; int right_maxBST_nodesnum = childtree_info[2];
//更新当前结点为根的子树的最大值、最小值 childtree_info[0]=Math.max(right_maxBST_max,curr.val); childtree_info[1]=Math.min(left_maxBST_min,curr.val); //根据左右子树递归信息,判断当前结点为根的子树性质:如果是二叉搜索树则更新最大二叉搜索子树的信息;如果不是,则返回左右子树中的最大二叉搜索子树的信息 if(left_maxBST==curr.left && right_maxBST==curr.right && left_maxBST_max<curr.val && curr.val<right_maxBST_min){ childtree_info[2]= left_maxBST_nodesnum + right_maxBST_nodesnum +1; return curr; }else{ childtree_info[2]=Math.max(left_maxBST_nodesnum,right_maxBST_nodesnum); return left_maxBST_nodesnum>right_maxBST_nodesnum?left_maxBST:right_maxBST; } }
二叉树进阶之寻找一棵二叉树中的最大二叉搜索子树