《演算法筆記》8. 二叉樹的遞迴思維實戰
阿新 • • 發佈:2020-07-31
[TOC]
# 1 二叉樹的遞迴套路
1、 可以解決面試中的絕大部分二叉樹(95%以上)的問題,尤其是樹形dp問題
2、 其本質是利用遞迴遍歷二叉樹的便利性,每個節點在遞迴的過程中可以回到該節點3次
==具體步驟為:==
1. 假設以X節點為頭,假設可以向X左樹和右樹要任何資訊
2. 在上一步的假設下,討論以X為頭結點的樹,得到答案的可能性(最重要),常見分類是與X無關的答案,與X有關的答案
3. 列出所有可能性後,確定到底需要向左樹和右樹要什麼樣的資訊
4. 把左樹資訊和右樹資訊求全集,就是任何一顆子樹都需要返回的資訊S
5. 遞迴函式都返回S,每顆子樹都這麼要求
6. 寫程式碼,在程式碼中考慮如何把左樹資訊和右樹資訊整合出整棵樹的資訊
## 1.1 二叉樹的遞迴套路深度實踐
### 1.1.1 例一:判斷二叉樹平衡與否
給定一棵二叉樹的頭結點head,返回這顆二叉樹是不是平衡二叉樹
> 平衡樹概念:在一棵二叉樹中,每一個子樹,左樹的高度和右樹的高度差不超過1
> 那麼如果以X為頭的這顆樹,要做到平衡,那麼X的左樹要是平衡的,右樹也是平衡的,且X的左樹高度和右樹高度差不超過1
> 所以該題,我們X需要向左右子樹要的資訊為,1.高度 2. 是否平衡
```Java
package class08;
public class Code01_IsBalanced {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
public static boolean isBalanced1(Node head) {
boolean[] ans = new boolean[1];
ans[0] = true;
process1(head, ans);
return ans[0];
}
public static int process1(Node head, boolean[] ans) {
if (!ans[0] || head == null) {
return -1;
}
int leftHeight = process1(head.left, ans);
int rightHeight = process1(head.right, ans);
if (Math.abs(leftHeight - rightHeight) > 1) {
ans[0] = false;
}
return Math.max(leftHeight, rightHeight) + 1;
}
public static boolean isBalanced2(Node head) {
return process2(head).isBalaced;
}
// 左、右要求一樣,Info 表示資訊返回的結構體
public static class Info {
// 是否平衡
public boolean isBalaced;
// 高度多少
public int height;
public Info(boolean b, int h) {
isBalaced = b;
height = h;
}
}
// 遞迴呼叫,X自身也要返回資訊Info。
// 解決X節點(當前節點)怎麼返回Info資訊
public static Info process2(Node X) {
// base case
if (X == null) {
return new Info(true, 0);
}
// 得到左樹資訊
Info leftInfo = process2(X.left);
// 得到右樹資訊
Info rightInfo = process2(X.right);
// 高度等於左右最大高度,加上當前頭結點的高度1
int height = Math.max(leftInfo.height, rightInfo.height) + 1;
boolean isBalanced = true;
// 左樹不平衡或者右樹不平衡,或者左右兩子樹高度差超過1
// 那麼當前節點為頭的樹,不平衡
if (!leftInfo.isBalaced || !rightInfo.isBalaced || Math.abs(leftInfo.height - rightInfo.height) > 1) {
isBalanced = false;
}
// 加工出當前節點的資訊返回
return new Info(isBalanced, height);
}
// for test
public static Node generateRandomBST(int maxLevel, int maxValue) {
return generate(1, maxLevel, maxValue);
}
// for test
public static Node generate(int level, int maxLevel, int maxValue) {
if (level > maxLevel || Math.random() < 0.5) {
return null;
}
Node head = new Node((int) (Math.random() * maxValue));
head.left = generate(level + 1, maxLevel, maxValue);
head.right = generate(level + 1, maxLevel, maxValue);
return head;
}
public static void main(String[] args) {
int maxLevel = 5;
int maxValue = 100;
int testTimes = 1000000;
for (int i = 0; i < testTimes; i++) {
Node head = generateRandomBST(maxLevel, maxValue);
if (isBalanced1(head) != isBalanced2(head)) {
System.out.println("Oops!");
}
}
System.out.println("finish!");
}
}
```
### 1.1.2 例二:返回二叉樹任意兩個節點最大值
給定一棵二叉樹的頭結點head,任何兩個節點之間都存在距離,返回整棵二叉樹的最大距離
> 1、有可能最大距離和當前節點X無關,即最大距離是X左樹的最大距離,或者右樹的最大距離
> 2、最大距離跟X有關,即最大距離通過X。左樹離X最遠的點,到X右樹上離X最遠的點。即X左樹的高度加上X自身高度1,加上X右樹上的高度
> 結論:那麼根據遞迴套路,我們每次遞迴,需要返回X左樹的最大距離和高度,同理返回X右樹的最大距離和高度。Info包含最大距離和高度
```Java
package class08;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
public class Code08_MaxDistance {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int data) {
this.value = data;
}
}
public static int maxDistance1(Node head) {
if (head == null) {
return 0;
}
A