二叉树的递归套路(重要)
假设以X节点为头,可以向X左树和X右树要任何信息
在上一步的假设下,讨论以X为头结点的树,得到答案的可能性(最重要)
列出所有可能性后,确定到底需要向左树和右树要什么信息
把左树信息和右树信息求全集,就是任何一棵子树都要返回的信息S
递归函数对于每棵子树都要返回S
在代码中考虑如何利用左树信息和右树信息整合出整棵树的信息
1.判断二叉树是否是平衡二叉树 平衡树(Balance Tree):任意节点的子树的高度差都小于等于1。
解题思路:
对于任意节点x,满足:
x左树平衡
x右树平衡
|x左树高度 - x右树高度| < 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public static boolean isBalanced (Node head) { return process(head).isBalanced; } public static class Info { public boolean isBalanced; public int height; public Info (boolean i, int h) { isBalanced = i; height = h; } } public static Info process (Node x) { if (x == null ) { return new Info(true , 0 ); } Info leftInfo = process(x.left); Info rightInfo = process(x.right); int height = Math.max(leftInfo.height, rightInfo.height) + 1 ; boolean isBalanced = true ; if (!leftInfo.isBalanced) { isBalanced = false ; } if (!rightInfo.isBalanced) { isBalanced = false ; } if (Math.abs(leftInfo.height - rightInfo.height) > 1 ) { isBalanced = false ; } return new Info(isBalanced, height); }
2.判断二叉树是否是搜索二叉树 二叉查找树(Binary Search Tree):每一棵子树均满足左树比头节点小,右树比头节点大。
解题思路:
对于任意节点x,满足:
x左树是否是搜索二叉树
x右树是否是搜索二叉树
x左树最大值 < x右树最小值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public static boolean isBST (Node head) { if (head == null ) { return true ; } return process(head).isBST; } public static class Info { public boolean isBST; public int max; public int min; public Info (boolean i, int ma, int mi) { isBST = i; max = ma; min = mi; } } public static Info process (Node x) { if (x == null ) { return null ; } Info leftInfo = process(x.left); Info rightInfo = process(x.right); int max = x.value; if (leftInfo != null ) { max = Math.max(max, leftInfo.max); } if (rightInfo != null ) { max = Math.max(max, rightInfo.max); } int min = x.value; if (leftInfo != null ) { min = Math.min(min, leftInfo.min); } if (rightInfo != null ) { min = Math.min(min, rightInfo.min); } boolean isBST = true ; if (leftInfo != null && !leftInfo.isBST) { isBST = false ; } if (rightInfo != null && !rightInfo.isBST) { isBST = false ; } if (leftInfo != null && leftInfo.max >= x.value) { isBST = false ; } if (rightInfo != null && rightInfo.min <= x.value) { isBST = false ; } return new Info(isBST, max, min); }
3.判断二叉树是否是完全二叉树 完全二叉树(Complete Binary Tree):叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。 满二叉树肯定是完全二叉树,而完全二叉树不一定是满二叉树。
解题思路:
非递归实现,按层遍历,每个节点判断:
有右子节点没有左子节点直接判断不是完全二叉树
如果存在一个节点不完全(有左无右),后续节点如果有子节点,直接判断不是完全二叉树
递归实现,对于节点x满足完全二叉树有以下四种情况:
左树满,右树满,左树高度 == 右树高度
左树完全,右树满,左树高度 == 右树高度 + 1
左树满,右树满,左树高度 == 右树高度 + 1
左树满,右树完全,左树高度 == 右树高度
合并,递归实现对于每个节点需要:
是否是满二叉树
是否是完全二叉树
高度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 public static boolean isCBT1 (Node head) { if (head == null ) { return true ; } Queue<Node> queue = new LinkedList<>(); boolean leaf = false ; Node l = null ; Node r = null ; queue.add(head); while (!queue.isEmpty()) { head = queue.poll(); l = head.left; r = head.right; if ( (leaf && (l != null || r != null )) || (l == null && r != null ) ) { return false ; } if (l != null ) { queue.add(l); } if (r != null ) { queue.add(r); } if (l == null || r == null ) { leaf = true ; } } return true ; } public static boolean isCBT2 (Node head) { if (head == null ) { return true ; } return process(head).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; } } public static Info process (Node x) { if (x == null ) { return new Info(true , true , 0 ); } Info leftInfo = process(x.left); Info rightInfo = process(x.right); int height = Math.max(leftInfo.height, rightInfo.height) + 1 ; boolean isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height; boolean isCBT = false ; if (isFull) { isCBT = true ; } else { if (leftInfo.isCBT && rightInfo.isCBT) { if (leftInfo.isCBT && rightInfo.isFull && leftInfo.height == rightInfo.height + 1 ) { isCBT = true ; } if (leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height + 1 ) { isCBT = true ; } if (leftInfo.isFull && rightInfo.isCBT && leftInfo.height == rightInfo.height) { isCBT = true ; } } } return new Info(isFull, isCBT, height); }
4.二叉树最大距离 节点距离:用最精简的走法从一个节点走到另一个节点经过的节点数(包含开始结束节点)
对于节点x最大距离有三种可能性,其中最大值即x最大距离:
x左树最大距离
x右树最大距离
x左树与x最远(左树高度) + x右树与x最远(x右树高度) + 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static int maxDistance2 (Node head) { return process(head).maxDistance; } public static class Info { public int maxDistance; public int height; public Info (int m, int h) { maxDistance = m; height = h; } } public static 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 ; int p1 = leftInfo.maxDistance; int p2 = rightInfo.maxDistance; int p3 = leftInfo.height + rightInfo.height + 1 ; int maxDistance = Math.max(Math.max(p1, p2), p3); return new Info(maxDistance, height); }
5.判断二叉树是否是满二叉树 满二叉树:二叉树每一个层的节点数都达到最大值,也就是说,如果二叉树的高度为 h,则结点总数是 2^h - 1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 public static boolean isFull1 (Node head) { if (head == null ) { return true ; } Info1 all = process1(head); return (1 << all.height) - 1 == all.nodes; } public static class Info1 { public int height; public int nodes; public Info1 (int h, int n) { height = h; nodes = n; } } public static Info1 process1 (Node head) { if (head == null ) { return new Info1(0 , 0 ); } Info1 leftInfo = process1(head.left); Info1 rightInfo = process1(head.right); int height = Math.max(leftInfo.height, rightInfo.height) + 1 ; int nodes = leftInfo.nodes + rightInfo.nodes + 1 ; return new Info1(height, nodes); } public static boolean isFull2 (Node head) { if (head == null ) { return true ; } return process2(head).isFull; } public static class Info2 { public boolean isFull; public int height; public Info2 (boolean f, int h) { isFull = f; height = h; } } public static Info2 process2 (Node h) { if (h == null ) { return new Info2(true , 0 ); } Info2 leftInfo = process2(h.left); Info2 rightInfo = process2(h.right); boolean isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height; int height = Math.max(leftInfo.height, rightInfo.height) + 1 ; return new Info2(isFull, height); }
6.最大搜索二叉子树大小(LeetCode 333.) 要求找到给定二叉树中所有BST子树节点数最大的子树。
对于任意节点x,要得到maxBSTSize,解题思路:
如果x不做头,需要比较:
x左树maxBSTSize
x右树maxBSTSize
如果x做头,需要判断:
左树是否是BST
右树是否是BST
左树最大值 < x
右树最小值 > x
左树size + 右树size + 1
合并,递归实现对于每个节点需要:
maxBSTSize,当前子树最大搜索二叉子树大小
max,当前子树中最大值
min,当前子树中最小值
size,当前子树节点数(maxBSTSize == size,子树本身是BST)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 public static int largestBSTSubtree (Node head) { if (head == null ) { return 0 ; } return process(head).maxBSTSubtreeSize; } public static class Info { public int maxBSTSubtreeSize; public int allSize; public int max; public int min; public Info (int m, int a, int ma, int mi) { maxBSTSubtreeSize = m; allSize = a; max = ma; min = mi; } } public static Info process (Node x) { if (x == null ) { return null ; } Info leftInfo = process(x.left); Info rightInfo = process(x.right); int max = x.val; int min = x.val; int allSize = 1 ; if (leftInfo != null ) { max = Math.max(leftInfo.max, max); min = Math.min(leftInfo.min, min); allSize += leftInfo.allSize; } if (rightInfo != null ) { max = Math.max(rightInfo.max, max); min = Math.min(rightInfo.min, min); allSize += rightInfo.allSize; } int p1 = -1 ; if (leftInfo != null ) { p1 = leftInfo.maxBSTSubtreeSize; } int p2 = -1 ; if (rightInfo != null ) { p2 = rightInfo.maxBSTSubtreeSize; } int p3 = -1 ; boolean leftBST = leftInfo == null ? true : (leftInfo.maxBSTSubtreeSize == leftInfo.allSize); boolean rightBST = rightInfo == null ? true : (rightInfo.maxBSTSubtreeSize == rightInfo.allSize); if (leftBST && rightBST) { boolean leftMaxLessX = leftInfo == null ? true : (leftInfo.max < x.val); boolean rightMinMoreX = rightInfo == null ? true : (x.val < rightInfo.min); if (leftMaxLessX && rightMinMoreX) { int leftSize = leftInfo == null ? 0 : leftInfo.allSize; int rightSize = rightInfo == null ? 0 : rightInfo.allSize; p3 = leftSize + rightSize + 1 ; } } return new Info(Math.max(p1, Math.max(p2, p3)), allSize, max, min); }
7.最大搜索二叉子树头结点 此题是上一题的变种,要求找到给定二叉树中所有BST子树节点数最大的子树的头结点。
对于任意节点x,要得到maxBSTHead,解题思路:
如果x不做头,需要比较maxBSTSize得到maxBSTHead:
x左树maxBSTSize,maxBSTHead
x右树maxBSTSize,maxBSTHead
如果x做头,需要判断:
左树是否是BST
右树是否是BST
左树最大值 < x
右树最小值 > x
左树size + 右树size + 1
合并,递归实现对于每个节点需要:
maxBSTSize,当前子树最大搜索二叉子树大小
max,当前子树中最大值
min,当前子树中最小值
maxSubBSTHead,最大搜索子树头节点(maxSubBSTHead == x.left/right,子树本身是BST)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public static Node maxSubBSTHead (Node head) { if (head == null ) { return null ; } return process(head).maxSubBSTHead; } public static class Info { public Node maxSubBSTHead; public int maxSubBSTSize; public int min; public int max; public Info (Node h, int size, int mi, int ma) { maxSubBSTHead = h; maxSubBSTSize = size; min = mi; max = ma; } } public static Info process (Node X) { if (X == null ) { return null ; } Info leftInfo = process(X.left); Info rightInfo = process(X.right); int min = X.value; int max = X.value; Node maxSubBSTHead = null ; int maxSubBSTSize = 0 ; if (leftInfo != null ) { min = Math.min(min, leftInfo.min); max = Math.max(max, leftInfo.max); maxSubBSTHead = leftInfo.maxSubBSTHead; maxSubBSTSize = leftInfo.maxSubBSTSize; } if (rightInfo != null ) { min = Math.min(min, rightInfo.min); max = Math.max(max, rightInfo.max); if (rightInfo.maxSubBSTSize > maxSubBSTSize) { maxSubBSTHead = rightInfo.maxSubBSTHead; maxSubBSTSize = rightInfo.maxSubBSTSize; } } if ((leftInfo == null ? true : (leftInfo.maxSubBSTHead == X.left && leftInfo.max < X.value)) && (rightInfo == null ? true : (rightInfo.maxSubBSTHead == X.right && rightInfo.min > X.value))) { maxSubBSTHead = X; maxSubBSTSize = (leftInfo == null ? 0 : leftInfo.maxSubBSTSize) + (rightInfo == null ? 0 : rightInfo.maxSubBSTSize) + 1 ; } return new Info(maxSubBSTHead, maxSubBSTSize, min, max); }
8.二叉树任意两节点最低公共祖先 给定一棵二叉树的头结点head,以及另外两个节点a和b,要求返回a和b的最低公共祖先。
解题思路:
递归套路情况分析:
与x无关,x不是最低汇聚点:
左树上汇聚
右树上汇聚
a,b不全
与x有关,x是汇聚点:
a,b分别在左右树
x本身就是a,b在左树或右树
x本身就是b,a在左树或右树
合并,递归实现对于每个节点需要:
是否发现a
是否发现b
是否是汇聚点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public static Node lowestAncestor (Node head, Node a, Node b) { return process(head, a, b).ans; } public static class Info { public boolean findA; public boolean findB; public Node ans; public Info (boolean fA, boolean fB, Node an) { findA = fA; findB = fB; ans = an; } } public static Info process (Node x, Node a, Node b) { if (x == null ) { return new Info(false , false , null ); } Info leftInfo = process(x.left, a, b); Info rightInfo = process(x.right, a, b); boolean findA = (x == a) || leftInfo.findA || rightInfo.findA; boolean findB = (x == b) || leftInfo.findB || rightInfo.findB; Node ans = null ; if (leftInfo.ans != null ) { ans = leftInfo.ans; } else if (rightInfo.ans != null ) { ans = rightInfo.ans; } else { if (findA && findB) { ans = x; } } return new Info(findA, findB, ans); }
9.派对的最大快乐值 在全公司中邀请员工参加派对,每个员工能带来的快乐值不一,在不能同时邀请直属上下级的情况下,返回整个派对能获得的最大快乐值。
员工定义如下:
1 2 3 4 class Employee { public int happy; public List<Employee> nexts; }
题目分析:
公司员工本身是一棵多叉树
从多叉树中挑选节点
不能同时选择父子节点
要求挑选节点累加值最大
递归套路,解题思路:
对于任意节点x:
如果选择x,x 最大累加值 = x.happy + sum(x 每一个子节点不被选择的最大累加值)
如果不选择x,x 最大累计值 = 0 + sum(max(x 每一个子节点被选择的最大累加值, x 每一个子节点不被选择的最大累加值))
合并,对于每个节点需要以下信息:
选择当前节点的最大累加值
不选择当前节点的最大累加值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public static int maxHappy (Employee head) { Info allInfo = process(head); return Math.max(allInfo.no, allInfo.yes); } public static class Info { public int no; public int yes; public Info (int n, int y) { no = n; yes = y; } } public static Info process (Employee x) { if (x == null ) { return new Info(0 , 0 ); } int no = 0 ; int yes = x.happy; for (Employee next : x.nexts) { Info nextInfo = process(next); no += Math.max(nextInfo.no, nextInfo.yes); yes += nextInfo.no; } return new Info(no, yes); }