文章目录
1 二叉树的递归套路
1、 可以解决面试中的绝大部分二叉树(95%以上)的问题,尤其是树形dp问题
2、 其本质是利用递归遍历二叉树的便利性,每个节点在递归的过程中可以回到该节点3次
具体步骤为:
- 假设以X节点为头,假设可以向X左树和右树要任何信息
- 在上一步的假设下,讨论以X为头结点的树,得到答案的可能性(最重要),常见分类是与X无关的答案,与X有关的答案
- 列出所有可能性后,确定到底需要向左树和右树要什么样的信息
- 把左树信息和右树信息求全集,就是任何一颗子树都需要返回的信息S
- 递归函数都返回S,每颗子树都这么要求
- 写代码,在代码中考虑如何把左树信息和右树信息整合出整棵树的信息
1.1 二叉树的递归套路深度实践
1.1.1 例一:判断二叉树平衡与否
给定一棵二叉树的头结点head,返回这颗二叉树是不是平衡二叉树
平衡树概念:在一棵二叉树中,每一个子树,左树的高度和右树的高度差不超过1
那么如果以X为头的这颗树,要做到平衡,那么X的左树要是平衡的,右树也是平衡的,且X的左树高度和右树高度差不超过1
所以该题,我们X需要向左右子树要的信息为,1.高度 2. 是否平衡
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.2 例二:返回二叉树任意两个节点最大值
给定一棵二叉树的头结点head,任何两个节点之间都存在距离,返回整棵二叉树的最大距离
1、有可能最大距离和当前节点X无关,即最大距离是X左树的最大距离,或者右树的最大距离
2、最大距离跟X有关,即最大距离通过X。左树离X最远的点,到X右树上离X最远的点。即X左树的高度加上X自身高度1,加上X右树上的高度
结论:那么根据递归套路,我们每次递归,需要返回X左树的最大距离和高度,同理返回X右树的最大距离和高度。Info包含最大距离和高度
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;
}
ArrayList<Node> arr = getPrelist(head);
HashMap<Node, Node> parentMap = getParentMap(head);
int max = 0;
for (int i = 0; i < arr.size(); i++) {
for (int j = i; j < arr.size(); j++) {
max = Math.max(max, distance(parentMap, arr.get(i), arr.get(j)));
}
}
return max;
}
public static ArrayList<Node> getPrelist(Node head) {
ArrayList<Node> arr = new ArrayList<>();
fillPrelist(head, arr);
return arr;
}
public static void fillPrelist(Node head, ArrayList<Node> arr) {
if (head == null) {
return;
}
arr.add(head);
fillPrelist(head.left, arr);
fillPrelist(head.right, arr);
}
public static HashMap<Node, Node> getParentMap(Node head) {
HashMap<Node, Node> map = new HashMap<>();
map.put(head, null);
fillParentMap(head, map);
return map;
}
public static void fillParentMap(Node head, HashMap<Node, Node> parentMap) {
if (head.left != null) {
parentMap.put(head.left, head);
fillParentMap(head.left, parentMap);
}
if (head.right != null) {
parentMap.put(head.right, head);
fillParentMap(head.right, parentMap);
}
}
public static int distance(HashMap<Node, Node> parentMap, Node o1, Node o2) {
HashSet<Node> o1Set = new HashSet<>();
Node cur = o1;
o1Set.add(cur);
while (parentMap.get(cur) != null) {
cur = parentMap.get(cur);
o1Set.add(cur);
}
cur = o2;
while (!o1Set.contains(cur)) {
cur = parentMap.get(cur);
}
Node lowestAncestor = cur;
cur = o1;
int distance1 = 1;
while (cur != lowestAncestor) {
cur = parentMap.get(cur);
distance1++;
}
cur = o2;
int distance2 = 1;
while (cur != lowestAncestor) {
cur = parentMap.get(cur);
distance2++;
}
return distance1 + distance2 - 1;
}
public static int maxDistance2(Node head) {
return process(head).maxDistance;
}
// 我们的信息,整棵树的最大距离和整棵树的高度
public static class Info {
public int maxDistance;
public int height;
public Info(int dis, int h) {
maxDistance = dis;
height = h;
}
}
// 以X节点为头
public static Info process(Node X) {
// base case
if (X == null) {
return new Info(0, 0);
}
// 默认从左树拿到我们需要的info
Info leftInfo