java面試演算法筆記- 07 二叉樹的遞迴
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資訊整合才是上述簡要概括
}