二叉树的递归套路
-
假设以X节点为头,假设可以向X左子树和X右子树要任何信息
-
在上一步的假设下,讨论以X为头节点的树,得到答案的可能性(最重要)也就是以答案结果跟X有没有关系为划分讨论
-
列出所有可能性后,确定到底需要向左子树和右子树要什么信息
-
把左子树信息和右子树信息求全集,就是任何一颗子树都需要返回的信息info
-
递归函数都返回info,每一棵子树都这么要求
-
写代码,在代码中考虑如何把左子树和右子树信息整合出整棵树的信息
例一:平衡二叉树
什么是平衡二叉树:
在一棵二叉树中,每一棵子树的左子树和右子树的高度差小于2,分解可得到:
左子树必须是平衡的
右子树必须是平衡的
左子树和右子树的高度差小于2
给定一棵二叉树的头节点head,返回这棵二叉树是不是平衡二叉树
根据套路(1)我们可以写代码
假设我们可以获取到左右子树的信息:是否为平衡二叉树、高度是多少
//信息类,代表,能从子树上获取什么信息
class Info{
private boolean blance;//当前子树是否平衡
private int height;//当前子树的高度,用来返回给头节点判断是否平衡的
public Info(boolean blance, int height){
this.blance = blance;
this.height = height;
}
public boolean isBlance(){
return blance;
}
public int getHeight(){
return height();
}
public void setBlance(boolean blance){
this.blance = blance;
}
public void setHeight(int height){
this.height = height;
}
}
class getBlanceTree{
public Info process(Node X){
//如果一个空树,肯定是平衡的,并且高度为0,返回
if(X == null){
return new Info(true, 0);
}
//在左子树上递归,得到左子树的信息:是否平衡,高度是多少
Info leftInfo = process(X.left);
//在右子树调用递归,获取右子树的信息
Info leftRight = process(X.right);
//接下来的代码就是在当前节点上进行判断
//当前的节点的高度=左/右子树中最大的的高度+1
int height = Math.max(leftInfo.getHeight, right.getHeight) + 1;
//设置当前是否平衡的变量为真
boolean blance = true;
//如果左子树、右子树上、当前的子树上,有一个不平衡的,当前x为头节点子树就不平衡
//判断当前子树是不是平衡:它左右子树的高度差是否小于2
if(!leftInfo.isBlance() || !rightInfo.isBlance() || Math.abs(left.height - right.height) >= 2){
blance = false;
}
return new Info(blance, height);
}
}
例二:最大距离
给定一棵二叉树的头节点head,任何两个节点之间都存在着距离,返回整个二叉树的最大距离
什么是距离
一个节点通过树杈走到另一个节点,中间最短需要经过的节点数,比如如下的树,要从a走到b,经过3个节点,ab距离就是3
最大距离的两种情况
假如和当前头节点X无关,整棵树的最大距离为左子树的最大距离或者右子树的最大距离,谁大就是谁
假如和当前头节点X有关,整棵树的最大距离为左子树的高度+右子树的高度+1
根据套路(1)我们可以写代码
假设我们可以获取到左右子树的信息:当前节点的最大距离是多少、高度是多少
class Info{
public int maxDistance;//当前节点的最大距离
public int height;//高度是多少
public Info(int maxD, int height){
this.maxDistance = maxD;
this.height = height;
}
}
class getMaxDistance{
public Info process(Node X){
if(X == null){
return new Info(0, 0);
}
//在左子树调用递归,获取左子树的信息,左子树上的最大距离,左子树的高度
Info leftInfo = process(X.left);
//在右子树调用递归,获取右子树的信息
Info rightInfo = process(X.right);
int height = Math.max(leftInfo.height, rightInfo.height) + 1;//当前X节点为头节点的子树的高度
//整个树的最大距离就是两种情况中最大的那个
int maxDistance = Math.max(
//假如和X无关就是左/右子树的最大距离之间的较大值
Math.max(leftInfo.maxDistance, rightInfo.maxDistance),
//假如和X有关,就是左子树高度+右子树高度+1
leftInfo.height + rightInfo.height + 1);
return new Info(maxDistance, height);
}
}
求满足搜索二叉子树的最大子树 —— 把左子树信息和右子树信息求全集
给定一棵二叉树的头节点head,返回这棵二叉树中最大的二叉搜索子树的头节点
什么是整体搜索二叉树
-
整个树上没有重复值
-
左树的值比当前的节点小,右树的值比当前节点大
-
从当前节点为头节点的每一棵子树都如此
分情况解决
-
如果最大搜索二叉树与X无关,就是X左 / 右子树上最大二叉树的较大树,这一情况永远成立!
-
如果与X有关,直接返回X,就是说X就是最大搜索二叉树的头节点,满足这个需要有如下条件
-
左边的子树必须是搜索二叉树
-
右边的子树必须是搜索二叉树
-
左边的最大值小于X,右边的最小值大于X
-
需要从左右子树上获取什么信息
左树
-
是否是最大搜索二叉树
-
最大搜索二叉树的Size
-
最大值
右树
-
是否是最大搜索二叉树
-
最大搜索二叉树的Size
-
最小值
为了方便代码编写,可以总结为子树的信息
-
是否是最大搜索二叉树
-
最大搜索二叉树的Size
-
最小值
-
最大值
class Info{
public boolean isAllBST;//是否是最大搜索二叉树
public int maxSubBSTSize;//最大搜索二叉树的节点个数
public int min;//整个树最小值
public int max;//整个树最大值
}
class maxBST{
public void process(Node X){
if(X == null){
return null;
}
//在当前节点的左右子树上调用递归函数,获取需要的值
Info leftInfo = process(X.left);
Info rightInfo = process(X.right);
//当前节点X为头节点的子树的最小值
int min = X.value;
//当前节点X为头节点的子树的最大值
int max = X.value;
//当左子树不为空,更新当前子树的最大最小值
if(leftInfo != null){
max = Math.max(max, leftInfo.max);
min = Math.min(min, leftInfo.min);
}
//当右子树不为空,更新当前子树的最大最小值
if(rightInfo != null){
max = Math.max(max, rightInfo.max);
min = Math.min(min, rightInfo.min);
}
//设置当前节点的最大搜索二叉树节点数为0
int maxSubBSTSize = 0;
//有左子树先设置进去,减少运行时间
if(leftInfo != null){
maxSubBSTSize = leftInfo.maxSubBSTSize;
}
//再去和右子树进行比较,得到更大的搜索二叉树的节点数
if(rightInfo != null){
maxSubBSTSize = Math.max(maxSubBSTSize, rightInfo.maxSubBSTSize);
}
boolean isAllBST = false;
//X节点为头节点的二叉树为搜索二叉树必须同时满足三个条件
if( //条件1,它的左子树为搜索二叉树,注意:空树也算搜索二叉树
(leftInfo == null ? true :leftInfo.isAllBST)
//((leftInfo != null && leftInfo.isAllBST == true) || leftInfo == null)
&& //条件2,它的右子树为搜索二叉树
(rightInfo == null? true : rightInfo.isAllBST)
//((rightInfo != null && leftInfo.isAllBST == true) || rightInfo == null)
&& //条件3,左子树最大值小于X.value,右子树最小值大于X.value,注意左右为空树的最大值最小值不用判断但是也要写上
(leftInfo == null ? true : leftInfo.max < X.value
&&
rightInfo == null ? true : rightInfo.min > X.value)
/*(leftInfo != null && leftInfo.max < X.value || leftInfo == null)
&&
(rightInfo != null && rightInfo.min > X.value || rightInfro == null)
)*/
{
//当前搜索二叉树的最大值,首先判断左右儿子是否为空
maxSubBSTSize = (leftInfo == null ? 0 : leftInfo.maxSubBSTSize) +
(rightInfo == null ? 0 : rightInfo.maxSubBSTSize) + 1;
isAllBST = true;
}
return new Info(isAllBST, maxSubBSTSize, min, max);
}
}
leetcode:
使用二叉树的递归思想解决,假设当前头节点为X
首先我们确定我们要在左右两子树上要什么信息——这棵以X为头节点的子树上是否有p,q节点(包括X自己)isHave,当前最近公共祖先LCANode
class Info{
public boolean isHave;
public TreeNode LCANode;
public Info(boolean have, TreeNode L){
isHave = have;
LCANode = L;
}
}
然后我们分类讨论,这个问题是否跟X有关,得到的答案——是的
-
和X无关
-
也就是说X点不是p/q节点
-
我们需要获取X的左右子树上是否有p和q节点,如果有,设置子树的Info.isHave为true
-
如果p、q两点,分别在X的左右子树上,则X就是最近公共祖先,LCANode = X
-
如果X的左右子树上产生了LCANode,就弹出对应的信息
-
-
和X有关
-
也就是说X是p/q节点
-
我们需要获取X的左右子树上是否有p/q节点,如果有子树info.isHave为true
-
如果p / q点在X的左/右子树上,X就是最近公共祖先,LCANode = X
-
如果X的左右子树上产生了LCANode,就弹出对应的信息
-
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
return process(root, p, q).LCANode;
}
public Info process(TreeNode X, TreeNode p, TreeNode q){
if(X == null){
return new Info(false, null);
}
Info leftInfo = process(X.left, p, q);
Info rightInfo = process(X.right, p, q);
Boolean isHave = false;
TreeNode LCANode = null;
//如果在左右子树找到了LCANode了就弹出对应的信息,不用再往下继续了
if(leftInfo.LCANode != null){
return new Info(leftInfo.isHave, leftInfo.LCANode);
}else if(rightInfo.LCANode != null){
return new Info(rightInfo.isHave, rightInfo.LCANode);
}
if(X == p || X == q){//如果X自己就是p/q
//如果X自己就是p/q isHave必定为True
isHave = true;
//这种情况下左右子树上必定不能产生LCANode,因为pq其中一个点就是X
LCANode = leftInfo.isHave || rightInfo.isHave ? X : LCANode;
}else{//如果X自己不是p/q
//X的左右子树上是否有p和q节点,如果有,设置子树的Info.isHave为true
isHave = leftInfo.isHave || rightInfo.isHave ? true : isHave;
//如果p、q两点,分别在X的左右子树上,则X就是最近公共祖先,LCANode = X
LCANode = leftInfo.isHave && rightInfo.isHave ? X : LCANode;
}
return new Info(isHave, LCANode);
}
}