Leetcode-树-递归学习练习笔记-代码

该练习为Leetcode经典题目,以简单为主,加了理解的注释
代码标准参考 http://www.cyc2018.xyz/

   
1. 树的高度 104
    public int maxDepth(TreeNode root){
        // 递归root左右子树, 返回最大值+1
        if(root == null)    return 0;
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }

2. 平衡树 Balanced Binary Tree  110
    // 左右子树高度差小于等于1,返回boolean
    private boolean result = true;

    public boolean isBalanced(TreeNode root) {
        maxDepth(root);
        return result;
    }
    // 递归遍历左右子树,在后序遍历位置 判断高度差是否小于1
    public int maxDepth(TreeNode root) {
        if (root == null) return 0;
        int l = maxDepth(root.left);
        int r = maxDepth(root.right);
        if (Math.abs(l - r) > 1) result = false;
        return 1 + Math.max(l, r);
    }

3. 两个节点的最长路径  Diameter of Binary Tree 543
    private int max = 0 
   
    public int diameterOfBinaryTree(TreeNode root){
        depth(root);
        return max;
    }
    // 递归遍历左右子树,在后序遍历位置维护全局变量
    private int depth(TreeNode root){
        if(root == null)    return 0;
        int leftDepth = depth(root.left);
        int rightDepth = depth(root.right);
        max = Math.max(max, leftDpeth + rightDepth);
        return Math.max(leftDepth, rightDepth) + 1;
    }

4. 翻转树 226 Invert Binary Tree
    //解法1
    public TreeNode invertTree(TreeNode root) {
        if (root == null) return null;
        TreeNode left = root.left;  // 后面的操作会改变 left 指针,因此先保存下来
        root.left = invertTree(root.right);
        root.right = invertTree(left);
        return root;
    }

    // 解法2
    public TreeNode invertTree(TreeNode root){
        if(root == null)    return null;
        // 按照定义反转二叉树
        TreeNode left = invertTree(root.left);
        TreeNode right = invertTree(root.right);
        // 按照定义指向翻转后的树
        root.left = right;
        root.right = left;
        return root;
    }

5. 归并两棵树 617 Merge Two Binary Tree
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2){
        //base case 空树,单棵树
        if(t1 == null && t2 == null)    return null;
        if(t1 == null)  return t2;
        if(t2 == null)  return t1;
        // 前序遍历位置,先操作根节点,再操作左右子树
        TreeNode root = new TreeNode(t1.val + t2.val);
        root.left = mergeTrees(t1.left, t2.left);
        root.right = mergeTrees(t1.right, t2.right);
        return root;
    }

6. 判断路径和是否等于一个数 112 Pathsum
    // 路径和定义为 从 root 到 leaf所有节点的和
    public boolean hasPathSum(TreeNode root, int sum){
        // base case: 空树 和 根节点满足
        if(root == null)    return false;
        if(root.left == null && root.right == null && root.val == sum)  return true;
        // 遍历左右子树
        return hasPathSum(root.left, sum - root.val)  || hasPathSum(root.right, sum - root.val);
    }

7. 统计路径和等于一个数的路径数量 437 Path Sum  
    // 路径不一定以 root 开头,也不一定以 leaf 结尾,但是必须连续。
    public int pathSum(TreeNode root, int sum){
        if(root == null)    return 0;
        // 先从根节点开始,再遍历左右子树
        int result = pathSumStartWithRoot(root, sum) + pathSum(root.left, sum) + pathSum(root.right ,sum);
        return result;
    }
    private int pathSumStartWithRoot(TreeNode root, int sum){
        if(root == null)    return 0;
        int ret = 0;
        if(root.val == sum) ret++;
        // 遍历左右子树
        ret += pathSumStartWith(root.left, sum - root.val) + pathSumStartWith(root.right, sum - root.val);
        return ret;
    }


    // 解法2 
    HashMap<Integer, Integer> preSumCount = new HashMap<>();
    int pathSum, targetSum;
    int res = 0;

    public int pathSum(TreeNode root, int targetSum) {
        if (root == null) {
            return 0;
        }
        this.pathSum = 0;
        this.targetSum = targetSum;
        this.preSumCount.put(0, 1);
        traverse(root);
        return res;
    }

    void traverse(TreeNode root) {
        if (root == null) {
            return;
        }
        // 前序遍历位置
        pathSum += root.val;
        // 从二叉树的根节点开始,路径和为 pathSum - targetSum 的路径条数
        // 就是路径和为 targetSum 的路径条数
        res += preSumCount.getOrDefault(pathSum - targetSum, 0);
        // 记录从二叉树的根节点开始,路径和为 pathSum 的路径条数
        preSumCount.put(pathSum, preSumCount.getOrDefault(pathSum, 0) + 1);

        traverse(root.left);
        traverse(root.right);

        // 后序遍历位置
        preSumCount.put(pathSum, preSumCount.get(pathSum) - 1);
        pathSum -= root.val;
    }

8. 子树 Subtree of Anoter Tree  572
    // 给两棵树,问第二棵是否是第一颗的子树
    public boolean isSubtree(TreeNode s, TreeNode t){
        if(s == null)   return false;
        return isSubtreeWithRoot(s, t) || isSubtree(s.left, t) || isSubtree(s.right, t);
    }
    private boolean isSubtreeWithRoot(TreeNode s, TreeNode t){
        if( t == null && s == null)  return true;
        if( t == null || s == null)  return false;
        return isSubtreeWithRoot(s.left, t.left) && isSubtreeWithRoot(s.right, t.right);
    }

9.  树的对称 Symmetric Tree  101
    public boolean isSymmetric(TreeNode root){
        if(root == null) return true;
        return isSymmetric(root.left ,root.right);
    }
    private boolean isSymmetrc(TreeNode t1, TreeNode t2){
        if(t1 == null && t2 == null )   return true;
        if(t1 == null || t2 == null)    return false;
        // 左子树的左节点和右子树的右节点比较;右节点和左节点比较
        return isSymmetric(t1.left, t2.right) && isSymmetric(t1.right, t2.left);
    }

10. 最小路径 Minimum Depth of Binary Tree 111
    // 树的根节点到叶子节点的最小路径长度
    // 先找左右子树的高度,比较最小值,+1
    public int minDepth(TreeNode root){
        if(root == null)    return 0;
        int left = minDepth(root.left);
        int right = minDepth(root.right);
        if(left == 0 || right == 0)     return left + right + 1;
        return Math.min(left, right )+ 1;
    }

11. 统计左叶子节点的和  404 sum of left leaves 
    public int sumOfLeftLeaves(TreeNode root){
        if(root == null)    return 0;
        // 如果根节点的左节点是叶子节点直接返回left + 遍历右子树
        if(isLeaf(root.left))   return root.left.val + sumOfLeftLeaves(root.right);
        // 如果存在左右子树,则都遍历
        return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right);
        
    }
    // 先判断是不是叶子节点
    private boolean isLeaf(TreeNode node ){
        if(node == null)    return false;
        return node.left == null && node.right == null;
    }


    // 解法2,常规递归方法
    class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        traverse(root);
        return sum;
    }
    // 记录左叶子之和
    int sum = 0;
    // 二叉树遍历函数
    void traverse(TreeNode root) {
        if (root == null) {
            return;
        }
        if (root.left != null &&
            root.left.left == null && root.left.right == null) {
            // 找到左侧的叶子节点,记录累加值
            sum += root.left.val;
        }
        // 递归框架
        traverse(root.left);
        traverse(root.right);
    }
}

12. 相同节点值的最大路径长度 Longest Univalue Path 687
    private int path = 0;

    public int longestUnivaluePath(TreeNode root){
        dfs(root);
        return path;
    }

    private int dfs(TreeNode root){
        if(root == null)    return 0;

        // 计算左右子树最长树枝长度
        int left = dfs(root.left);
        int right = dfs(root.right);

        // 后序遍历位置更新。如果值不为空且和上级值相同,才是同值数值
        int leftPath = root.left != null && root.left.val == root.val ? left + 1 : 0;
        int rightPath = root.right != null && root.right.val == root.val ? right + 1 : 0;
        path = Math.max(path, leftPath + rightPath);
        // 实现函数定义,等于左右子树最长数值长度的最大值加上root节点本身
        return Math.max(leftPath, rightPath);
    }


    // 解法2
    class Solution {
    int res = 0;
    public int longestUnivaluePath(TreeNode root) {
        if (root == null) {
            return 0;
        }
        // 在后序遍历的位置更新 res
        maxLen(root, root.val);
        return res;
    }

    // 定义:计算以 root 为根的这棵二叉树中,从 root 开始值为 parentVal 的最长树枝长度
    private int maxLen(TreeNode root, int parentVal) {
        if (root == null) {
            return 0;
        }
        // 利用函数定义,计算左右子树值为 root.val 的最长树枝长度
        int leftLen = maxLen(root.left, root.val);
        int rightLen = maxLen(root.right, root.val);

        // 后序遍历位置顺便更新全局变量
        // 同值路径就是左右同值树枝长度之和
        res = Math.max(res, leftLen + rightLen);
        // 如果 root 本身和上级值不同,那么整棵子树都不可能有同值树枝
        if (root.val != parentVal) {
            return 0;
        }
        // 实现函数的定义:
        // 以 root 为根的二叉树从 root 开始值为 parentVal 的最长树枝长度
        // 等于左右子树的最长树枝长度的最大值加上 root 节点本身
        return  1 + Math.max(leftLen, rightLen);
    }
}

13. 间隔遍历 House Robber 337 打家劫舍,一维数组动态规划
    Map<TreeNode, Integer> cache = new HashMap<>();

    public int rob(TreeNode root){
        if(root == null)    return 0;
        // 动态规划,利用备忘录消除重叠子问题
        if(cache.containsKey(root)) return cache.get(root);
        // 抢劫,间隔一个
        int val1 = root.val;
        if(root.left != null)   val1 += rob(root.left.left) + rob(root.left.right);
        if(root.right != null)   val1 += rob(root.right.left) + rob(root.right.right);
        // 没抢的
        int val2 = rob(root.left) + rob(root.right);
        // 然后比较已抢劫和没抢的哪个钱多
        int res = Math.max(val1, val2);
        cache.put(root, res);
        return res;
    }

14. 找出二叉树中第二小的节点 Second Minimum Node In a Binary Tree 671
    // 一个节点要么具有0个或者2个子节点。如果有子节点,那么根节点是最小节点
    public int findSecondMinimumValue(TreeNode root){
        if(root == null)    return -1;
        if(root.left == null && root.right == null)     return -1;
        // base case

        int leftVal = root.left.val;
        int rightVal = root.right.val;

        // 递归调用左右子树查找最小值
        if(leftVal == root.val) leftVal = findSecondMinimumValue(root.left);
        if(rightVal == root.val) rightVal = findSecondMinimumValue(root.right);
        // 判断条件,看左右子树的最小值哪个更小
        if(leftVal != -1 && rightVal != -1) return Math.min(leftVal, rightVal);
        if(leftVal != -1)   return leftVal;
        return rightVal;
    }


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值