07 二叉树的递归
可以解决面试中绝大多数的二叉树问题尤其是树型dp问题
本质是利用递归遍历二叉树的便利性
二叉树的递归套路
1)假设以X节点为头,假设可以向X左树和X右树要任何信息
2)在上一步的假设下,讨论以X为头节点的树,得到答案的可能性(最重要) 常见分类: 与x无关,与x有关
3)列出所有可能性后,确定到底需要向左树和右树要什么样的信息
4)把左树信息和右树信息求全集,就是任何一棵子树都需要返回的信息S
5)递归函数都返回S,每一棵子树都这么要求
6)写代码,在代码中考虑如何把左树的信息和右树信息整合出整棵树的信息
难点:
1、假设分类
2、具体的可能性,需要什么具体信息
3、如何实现递归函数
1、base case
2、递归左右树
3、加工该次如何返回要求信息
题目
1、isBalance
给定一棵二叉树的头节点head,返回这颗二叉树是不是平衡二叉树
1、列举全部可能
1、) 左树
树高
是否为平衡二叉树
2、)右树
树高
是否为平衡二叉树
2、需要信息就是树高,和是否为平衡二叉树(画递归树)
Info process(Node head){
//base case
if(x == null){
return 0; //树高为0
}
//递归
Info(x.left);
Info(x.right);
//处理该次的信息返回
height = max(左树高,右树高) + 1;
if(左树.isAK
&& 右树.isAK
&& abs(左树.height - 右树.height)<=1){
Info(x).isAk = true;
}
}
2、is满二叉树
给出一个head,返回这颗树是否是满二叉树
method1、暴力解法
1、找到树高height
2、遍历树节点个数nodes
3、 if( nodes == (1>>height - 1)) yes else no
method2、树递归
举例所有的可能性:
1、head左、右子树,
树高 , 节点数
所以info为树高、节点数
Info process(Node head){
//base case
if(head == null){
return new Info(0,0);
}
//递归
process(head.left);
process(head.right);
//处理此次的信息
int height = max(process(head.left).height ,
process(head.right).height ) + 1;
int nodes = process(head(left).nodes
+process(head(right).nodes+1;
return new Info(height ,nodes);
}
void isFull(){
process(head);
if( nodes == (1>>height - 1))
yes else no;
}
3、is完全二叉树
method1、暴力解法
情况1、有右孩子没有左孩子
2、有左右孩子不双全,后序又出现孩子的情况!
其余情况都是满足条件
使用宽度优先遍历,之后使用flag1记录左右孩子不双全的情况,出现情况1、2就退出!
method2、树递归方法
举例所有答案
1、)满二叉树
2、)有缺口
1:左树没有撑满,右树为满二叉树
2:左树撑满,但是没有到右树,左右树都是满二叉树
3:左树撑满,右树为完全二叉树
转为数学表达式:
2.1 left.height - right.height = 1
左.isCBT(完全) 右.isFull(满)
可能会需要的信息: 树的结点数,用于判断isFull。但是没有必要,因为,当左树,右树都是full,且高度差为0,该树必定为满二叉树。base case 时,full 为 true。所以之前的题可以用这个思想去优化!!!
同理2.2 ,2.3都需要上述的height,isFull,isCBT的三个信息
public static class Info {
public boolean isFull; //满
public boolean isCBT; //完全
public int height; //高度
public Info(boolean full, boolean cbt, int h) {
isFull = full;
isCBT = cbt;
height = h;
}
}
Info process(Node head){
//base case
if(head == null){
return new
}
//递归
Info leftInfo = process(head.left);
Info rightInfo = process(head.right);
//处理次次消息
int height = max(leftInfo.height ,
rightInfo.height ) + 1;
boolean isFull =false;
if(leftInfo.isFull
&&
rightInfo.isFull
&&
leftInfo.height
==
rightInfo.height){
isFull = true;}
isCBT = false ;
if(isFull){
isCBT = true;
}else{
//1
if( leftInfo.isCBT
&&
rightInfo.isFull
&&
leftInfo.height -
rightInfo.height == 1){
isCBT = true;}
//2
if(leftInfo.isFull
&&
rightInfo.isFull
&&
leftInfo.height -
rightInfo.height == 1){
isCBT = true;}
//3
if(leftInfo.isFull
&&
rightInfo.isCBT
&&
leftInfo.height -
rightInfo.height == 0{
isCBT = true;}
}
return Info(isFull, isCBT, height);
}
4、返回最大距离
给定一棵二叉树的头节点head,任何两个节点之间都存在距离,返回整棵二叉树的最大距离
1、)最大距离 列举答案所有可能假设分类:
1、与head无关
需要左最大距离,左max
需要右最大距离,右max
最大距离 = (左max,右max)
2、与head有关
换言之就是俩棵最深树的距离
最大距离 = 左树高+1+右树高
树高 = max(左数高 ,右树高) + 1
3种情况
2、)需要信息
1、树的高度
2、点的最大距离
Info process(Node x){
//base case
if(x == null){
return new Info( 0 ,0);
}
//递归
Info(x.left);
Info(x.right);
//处理本次的信息sf
height = max(左树高,右树高) + 1;
distance = 左树高 + 右树高 + 1 ;
maxDistance = max(distance,max(Info(x.left).maxDistance,Info(x.right).maxDistance))
return new Info(height , maxDistance)
}
剩下的举例见算法题!
5、公共父节点
给定一棵二叉树的头节点head,和另外两个节点o1和o2。返回o1和o2的最低公共祖先
method1、暴力解
1、使用map记录每个键值对<子节点,父节点>
2、使用set,将a的节点的所有父节点加入set
3、遍历b的父节点,set.contain(b.父节点) 判断是否有重复
method2、二叉树递归
分析所有答案情况:
1、 o1,o2不在head
2、 o1,o2有一个在head的子树上 (都没有交汇)
3、o1,o2都在head的子树上 (必有交汇点)
1)全在左子树
2)全在右子树
3)左右个一个
4)o1 or o2为head
需要的信息:
树上有无o1,o2,交汇点
public static class Info {
//第一个交会的父节点
public Node ans;
//发现01
public boolean findO1;
//发现02
public boolean findO2;
public Info(Node a, boolean f1, boolean f2) {
ans = a;
findO1 = f1;
findO2 = f2;
}
}
Info process(Node head){
//base case
if(head == null){
return new Info(null,false,false);
}
Info leftInfo = process(head.left);
Info rightInfo = process(head.right);
//3信息
boolean findO1 = head == o1 || leftInfo.findO1 || rightInfo.findO1;
boolean findO2 = head == o2 || leftInfo.findO2 || rightInfo.findO2;
//3信息整合才是上述简要概括
}