【剑指Offer】个人学习笔记_55 - I_二叉树的深度&55 - II_平衡二叉树

刷题日期:下午4:25 2021年5月18日星期二

个人刷题记录,代码收集,来源皆为leetcode

经过多方讨论和请教,现在打算往Java方向发力

主要答题语言为Java

题目:

剑指 Offer 55 - I. 二叉树的深度

难度简单118

输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。

例如:

给定二叉树 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7

返回它的最大深度 3 。

提示:

  1. 节点总数 <= 10000
题目分析

至少也得一次遍历,然后遍历时定义一个全局变量,每下一层就加1,定义一个最大值不断比较。

初始解答:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    int count = 1, res = 1; //当前深度和最大深度
    public int maxDepth(TreeNode root) {
        if(root == null) {
            return 0;
        }
        dfs(root);
        return res;
    }
    private void dfs(TreeNode root) {
        if(root == null) {
            count = 1;
            return;
        } else {
            count ++;
            if(root.left != null && root.right != null) {
                
                dfs(root.left);
            }
            if(root.left != null && root.right == null) {
                // count ++;
                dfs(root.left);
            }
            if(root.left == null && root.right != null) {
                // count ++;
                dfs(root.right);
            }
            if(root.left == null && root.right == null) return;
        }
        res = Math.max(count, res);
        return;
    }
}

我也不知道怎么就能把两个元素的算出来三层

添加备注 输入: [1,2]

输出: 3

预期结果: 2

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int maxDepth(TreeNode root) {
        return root == null ? 0 : Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}

自叹不如,哎。 执行结果:通过

显示详情 添加备注

执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户

内存消耗:38.1 MB, 在所有 Java 提交中击败了94.61%的用户

层序遍历:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        //定义存放树节点的链表,后面没看懂
        List<TreeNode> queue = new LinkedList<>() {{ add(root);}}, tmp;
        int res = 0; //计数器
        while(!queue.isEmpty()) {
            //队列非空
            tmp = new LinkedList<>(); //临时队
            for(TreeNode node : queue) {
                //原来都可以这么遍历
                if(node.left != null) tmp.add(node.left); //只要有就加
                if(node.right != null) tmp.add(node.right);
            }
            queue = tmp; //下一层
            res++; //计数器自增
        }
        return res;
    }
}

执行结果: 通过

显示详情 添加备注

执行用时:2 ms, 在所有 Java 提交中击败了10.13%的用户

内存消耗:38.1 MB, 在所有 Java 提交中击败了94.97%的用户

学习他人:

方法一:

mata川L5 (编辑过)2020-02-13 递归,java一行解决

class Solution {
    public int maxDepth(TreeNode root) {
        return root == null ? 0 : Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}

方法二:

jackelyjL1 2020-03-03

这种题如果面试时出,多半会要求你用非递归写法

class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        Stack<Pair<TreeNode, Integer>> stack = new Stack<>();
        stack.add(new Pair<>(root, 1));
        int h = 0;
        while (!stack.isEmpty()) {
            Pair<TreeNode, Integer> pair = stack.pop();

            h = Math.max(pair.getValue(), h);

            if (pair.getKey().right != null) {
                stack.push(new Pair<>(pair.getKey().right, pair.getValue() + 1));
            }
            if (pair.getKey().left != null) {
                stack.push(new Pair<>(pair.getKey().left, pair.getValue() + 1));
            }
        }
        return h;
    }
}

方法三:

Aurora 2020-05-12 java

//递归
class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        int nLeft = maxDepth(root.left);
        int nRight = maxDepth(root.right);
        return nLeft > nRight ? nLeft + 1 : nRight + 1;
    }
}
//层次遍历
class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        Queue<TreeNode> queue = new LinkedList<>(){{add(root);}};
        int depth = 0;
        while(!queue.isEmpty()){
            int cursize = queue.size();
            for(int i = 0; i < cursize; i++){
                TreeNode temp = queue.poll();
                if(temp.left != null) queue.add(temp.left);
                if(temp.right != null) queue.add(temp.right);
            }
            depth++;
        }
        return depth;
    }
}

方法四:

K神 树的遍历方式总体分为两类:深度优先搜索(DFS)、广度优先搜索(BFS);

常见的 DFS : 先序遍历、中序遍历、后序遍历;

常见的 BFS : 层序遍历(即按层遍历)。

  • 求树的深度需要遍历树的所有节点,本文将介绍基于 后序遍历(DFS)层序遍历(BFS) 的两种解法。

作者:jyd
链接:https://leetcode-cn.com/problems/er-cha-shu-de-shen-du-lcof/solution/mian-shi-ti-55-i-er-cha-shu-de-shen-du-xian-xu-bia/
来源:力扣(LeetCode)

后序遍历(DFS)
  • 树的后序遍历 / 深度优先搜索往往利用 递归 或 栈 实现,本文使用递归实现。
  • 关键点: 此树的深度和其左(右)子树的深度之间的关系。显然,此树的深度 等于 左子树的深度 与 右子树的深度 中的 最大值 +1 。
class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}
层序遍历(BFS)
  • 树的层序遍历 / 广度优先搜索往往利用 队列 实现。
  • 关键点: 每遍历一层,则计数器 +1 ,直到遍历完成,则可得到树的深度。
class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        List<TreeNode> queue = new LinkedList<>() {{ add(root); }}, tmp;
        int res = 0;
        while(!queue.isEmpty()) {
            tmp = new LinkedList<>();
            for(TreeNode node : queue) {
                if(node.left != null) tmp.add(node.left);
                if(node.right != null) tmp.add(node.right);
            }
            queue = tmp;
            res++;
        }
        return res;
    }
}

题目:

剑指 Offer 55 - II. 平衡二叉树

难度简单149

输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

示例 1:

给定二叉树 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7

返回 true

示例 2:

给定二叉树 [1,2,2,3,3,null,null,4,4]

       1
      / \
     2   2
    / \
   3   3
  / \
 4   4

返回 false

限制:

  • 0 <= 树的结点个数 <= 10000
题目分析

基于上一题的话,就是不断判断左右子树的深度,然后比较res是否>1。

初始解答:

class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root == null) return false;
        if((maxDepth(root.left) - maxDepth(root.right)) > 1) return false;
        else return true;
        return isBalanced(root.left) && isBalanced(root.right);
    }
    private int maxDepth(TreeNode root) {
        if(root == null) return 0;
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}

Line 10: error: unreachable statement return true && isBalanced(root.left) && isBalanced(root.right);

没懂错在写法上了吗,返回的不就是布尔型变量么。

参考方法一改给判断加上了绝对值,去掉了else,然后就好了。

class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root == null) return true;
        if(Math.abs(maxDepth(root.left) - maxDepth(root.right)) > 1) return false;
        return isBalanced(root.left) && isBalanced(root.right);
    }
    private int maxDepth(TreeNode root) {
        if(root == null) return 0;
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}

执行结果:通过

显示详情 添加备注

执行用时:1 ms, 在所有 Java 提交中击败了100.00%的用户

内存消耗:38.3 MB, 在所有 Java 提交中击败了89.67%的用户

究极一行写法:

class Solution {
    public boolean isBalanced(TreeNode root) {
        return root == null ? true : Math.abs(maxDepth(root.left) - maxDepth(root.right)) <= 1 && isBalanced(root.left) && isBalanced(root.right);
    }
    private int maxDepth(TreeNode root) {
        return root == null ? 0 : Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}

执行结果:通过

显示详情 添加备注

执行用时:1 ms, 在所有 Java 提交中击败了100.00%的用户

内存消耗:38.5 MB, 在所有 Java 提交中击败了58.68%的用户

学习他人:

方法一:

梦小冷L1

2020-02-17

class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root==null) return true;
        if(Math.abs(getHigh(root.left)-getHigh(root.right))<=1){
            return isBalanced(root.left)&&isBalanced(root.right);
        }
        return false;
    }
    
    private int getHigh(TreeNode root){
        if(root==null) return 0;
        return Math.max(getHigh(root.left),getHigh(root.right))+1;
    }
}

方法二:

RaymondL1 1 小时前

class Solution {
    public boolean isBalanced(TreeNode root) {
        if(travel(root)>=0){
            return true;
        }
        return false;
    }
    public int travel(TreeNode root){
        if(root==null){return 0;}
        int left = travel(root.left)+1;
        int right = travel(root.right)+1;
        if(Math.abs(left-right)<=1){
            return Math.max(left, right);
        }
        return -100;
    }
}

方法三:

唐東L1 2021-04-05

我做出来第第一道递归题,纪念一下,果然刷题出奇迹,昨天还云里雾里,今天就优点眉目了。哈哈哈哈

class Solution {
    public boolean isBalanced(TreeNode root) {
        //需要左右树的高度和是否平衡树
        if(root == null)    return true;
        int leftD = depth(root.left);
        int rightD = depth(root.right); //左右子树深度

        return isBalanced(root.left) && isBalanced(root.right) && Math.abs(leftD - rightD) < 2;
    }

    public int depth(TreeNode zroot){
        if (zroot == null)
            return 0; 

        int left_depth = depth(zroot.left); //递归
        int right_depth = depth(zroot.right);
        return Math.max(left_depth, right_depth) + 1;
    }
}

方法四:

苏幕遮L2 2021-04-30 年轻人用数组来引用 来剪枝 来偷袭

public boolean isBalanced(TreeNode root) {
        boolean res[] = new boolean[]{true};
        deep(root, res);
        return res[0];
    }

    public int deep(TreeNode root, boolean res[]) {
        if (root == null || res[0] == false) {
            return 0;
        }
        int dl = deep(root.left, res);
        int dr = deep(root.right, res);
        if (Math.abs(dl - dr) > 1) {
            res[0] = false;
            return 0;
        } else {
            return Math.max(dl, dr) + 1;
        }
    }

方法五:

K神 以下两种方法均基于以下性质推出: 此树的深度 等于 左子树的深度右子树的深度 中的 最大值 +1 。

后序遍历 + 剪枝 (从底至顶)

此方法为本题的最优解法,但剪枝的方法不易第一时间想到。

思路是对二叉树做后序遍历,从底至顶返回子树深度,若判定某子树不是平衡树则 “剪枝” ,直接向上返回。

作者:jyd
链接:https://leetcode-cn.com/problems/ping-heng-er-cha-shu-lcof/solution/mian-shi-ti-55-ii-ping-heng-er-cha-shu-cong-di-zhi/
来源:力扣(LeetCode)

class Solution {
    public boolean isBalanced(TreeNode root) {
        return recur(root) != -1;
    }

    private int recur(TreeNode root) {
        if (root == null) return 0;
        int left = recur(root.left);
        if(left == -1) return -1;
        int right = recur(root.right);
        if(right == -1) return -1;
        return Math.abs(left - right) < 2 ? Math.max(left, right) + 1 : -1;
    }
}

莫非L3 (编辑过)2020-06-16

感谢分享,这里对第一种方法的Java代码根据个人理解加了一些注释,应该更容易理解:

public class Solution55_2 {

    public boolean isBalanced(TreeNode root) {
        return dfs(root)==-1?false:true;
    }

    //用left,right记录root左右子节点的深度,避免遍历root时对左右节点的深度进行重复计算。
    //考虑到需要同时记录各个节点的深度和其是否符合平衡性要求,这里的返回值设为int,用一个特殊值-1来表示出现不平衡的节点的情况,而不是一般采用的boolean
    public int dfs(TreeNode root){
    	//用后序遍历的方式遍历二叉树的每个节点(从底至顶),先左子树,再右子树,最后根节点,
        if(root==null) return 0;//root等于0时,该节点符合要求,返回其深度0,而不返回-1;
        int left = dfs(root.left);//left最开始的取值为0,从底朝上遍历,先左子树,后右子树,最后根节点
        if(left==-1) return -1;//若出现节点的深度为-1,则进行剪枝,开始向上返回,之后的迭代不再进行
        int right = dfs(root.right);
        if(right==-1) return -1;
        return Math.abs(right-left)<2?Math.max(left,right)+1:-1;//+1不能少
        //最开始计算的是左子树最左侧的一个叶节点,其左右子节点不存在,left=0,right=0,满足条件,返回该叶节点的深度max(0,0)+1=1;
    }
}

如有不对还请指正,谢谢。

先序遍历 + 判断深度 (从顶至底)

此方法容易想到,但会产生大量重复计算,时间复杂度较高。

思路是构造一个获取当前子树的深度的函数 depth(root) (即 面试题55 - I. 二叉树的深度 ),通过比较某子树的左右子树的深度差 abs(depth(root.left) - depth(root.right)) <= 1 是否成立,来判断某子树是否是二叉平衡树。若所有子树都平衡,则此树平衡。

class Solution {
    public boolean isBalanced(TreeNode root) {
        if (root == null) return true;
        return Math.abs(depth(root.left) - depth(root.right)) <= 1 && isBalanced(root.left) && isBalanced(root.right);
    }

    private int depth(TreeNode root) {
        if (root == null) return 0;
        return Math.max(depth(root.left), depth(root.right)) + 1;
    }
}

总结

以上就是本题的内容和学习过程了,二叉树的遍历也是面试中常见的考点,还是要掌握的。

欢迎讨论,共同进步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值