二叉树问题求解

本文介绍了如何利用树形动态规划(DP)和递归策略解决二叉树相关问题,如判断平衡性、验证二叉搜索树以及寻找最近公共祖先。通过三个具体的例子详细阐述了思路和模板,每个例子中都包含后序遍历的思想,并展示了如何利用子节点信息维护父节点状态。
摘要由CSDN通过智能技术生成

在求解一般树形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);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值