在求解一般树形dp的问题可以考虑使用递归求解 -》 后序遍历思想
树形dp问题:从子结点向上维护出父结点状态
模板:
public class Main {
public class Info {
// 用于维护的结点信息
}
public Info process(Node root, ...) {
if (root == null) {
return new Info();
}
Info leftInfo = process(root.left);
Info rightInfo = process(root.right);
// 利用子结点维护当前结点状态
return new Info();
}
}
例1 判断平衡性
思路:每次以当前结点x考虑
a. 不考虑x
1. 左子树平衡
2. 右子树平衡
b. 考虑x -》 维护状态
1. 左子树,右子树都平衡,且高度差不大于1
class Solution {
public class Info {
boolean isBalance; // 是否平衡
int height; // 树高度,用于之后判断子树高度差
public Info() {
}
public Info(boolean isBalance, int height) {
this.isBalance = isBalance;
this.height = height;
}
}
public boolean isBalanced(TreeNode root) {
Info res = isBalancedHelp(root);
return res.isBalance;
}
public Info isBalancedHelp(TreeNode root) {
if (root == null) { // 边界
return new Info(true, 0);
}
Info leftInfo = isBalancedHelp(root.left); // 左子树信息
Info rightInfo = isBalancedHelp(root.right); // 右子树信息
// 维护当前结点状态
int height = Math.max(leftInfo.height, rightInfo.height) + 1;
boolean isBalance = true;
if (!leftInfo.isBalance) isBalance = false;
if (!rightInfo.isBalance) isBalance = false;
if (Math.abs(leftInfo.height - rightInfo.height) > 1) isBalance = false;
return new Info(isBalance, height);
}
}
例2 验证二叉搜索树
思路:
a. 不考虑x
1. 左子树是二叉搜索树?
2. 右子树是二叉搜索树?
b. 考虑x
1. 当前结点x.val, 左子树最大值 < x.val? 右子树最小值 > x.val ?
维护Info属性:isBalanced,是否平衡; max/min:结点最大值和最小值
class Solution {
public class Info {
boolean isBST;
int max;
int min;
public Info() {}
public Info(boolean isBST, int max, int min) {
this.isBST = isBST;
this.max = max;
this.min = min;
}
}
public boolean isValidBST(TreeNode root) {
return process(root).isBST;
}
public Info process(TreeNode root) {
if (root == null) {
return null;
}
Info leftInfo = process(root.left);
Info rightInfo = process(root.right);
int max = root.val;
int min = root.val;
if (leftInfo != null) {
max = Math.max(max, leftInfo.max);
min = Math.min(min, leftInfo.min);
}
if (rightInfo != null) {
max = Math.max(max, rightInfo.max);
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 >= root.val) isBST = false;
if (rightInfo != null && rightInfo.min <= root.val) isBST = false;
return new Info(isBST, max, min);
}
}
例3: 二叉树的最近公共祖先
思路:
方案一:用集合Map,Set统计p所有可能的祖先结点,然后用q进行遍历
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
Map<TreeNode, TreeNode> map = new HashMap<>();
findParent(map, root);
Set<TreeNode> set = new HashSet<>();
while (!set.contains(p)) {
set.add(p);
p = map.get(p);
}
while (!set.contains(q)) {
q = map.get(q);
}
return q;
}
public void findParent(Map<TreeNode, TreeNode> map, TreeNode root) {
if (root.left != null) {
map.put(root.left, root);
findParent(map, root.left);
}
if (root.right != null) {
map.put(root.right, root);
findParent(map, root.right);
}
}
}
思路2:递归求解 -》 后序遍历
不考虑x
1. 左子树中找到祖先
2. 右子树中找到祖先
3. 左右都没有
考虑x
1.有p, q,祖先就是x
2.x == p, 有q,祖先是x
3.x == q,有p, 祖先是x
class Solution {
public class Info {
boolean findP;
boolean findQ;
TreeNode ans;
public Info(boolean findP, boolean findQ, TreeNode ans) {
this.findQ = findQ;
this.findP = findP;
this.ans = ans;
}
}
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
return process(root, p, q).ans;
}
public Info process(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) {
return new Info(false, false, null);
}
Info leftInfo = process(root.left, p, q);
Info rightInfo = process(root.right, p, q);
boolean findP = (leftInfo.findP || root == p || rightInfo.findP);
boolean findQ = (leftInfo.findQ || root == q || rightInfo.findQ);
TreeNode ans = null;
if (leftInfo.ans != null) { // 前两种说的任一子树已经找到答案,直接赋值即可
ans = leftInfo.ans;
} else if (rightInfo.ans != null) {
ans = rightInfo.ans;
} else {
if (findQ && findP) { // 说明p,q分布于左右子树,那么公共祖先就是root
ans = root;
}
}
return new Info(findP, findQ, ans);
}
}