zoukankan      html  css  js  c++  java
  • LeetCode算法题-Maximum Depth of Binary Tree

    这是悦乐书的第164次更新,第166篇原创

    01 看题和准备

    今天介绍的是LeetCode算法题中Easy级别的第23题(顺位题号是104)。给定二叉树,找到它的最大深度。最大深度是从根节点到最远叶节点的最长路径上的节点数。叶子是没有子节点的节点。

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

        3
       / 
      9  20
         / 
        15  7
    

    返回其深度= 3。

    本次解题使用的开发工具是eclipse,jdk使用的版本是1.8,环境是win7 64位系统,使用Java语言编写和测试。

    02 第一种解法

    最大深度是根节点到最远的叶子节点路径上包含的节点数,以上面的二叉树为例,最长路径有两条:3->20->15,3->20->7,这两条路径上的节点数都是3个,所以最后得出其深度是3的结论。

    特殊情况一:当传入的二叉树为空时,它没有任何节点,它的深度是0。

    特殊情况二:只有根节点的时候,它的深度是1,只有它自身一个节点。

    正常情况:我们可以一步一步试着推导下,一个简单的二叉树深度计算过程,还是以上面的二叉树为例。

    从根节点开始,此时节点数为1,因为只有它一个节点。

    进入根节点的子节点,此时最长路径就是计算9这个左子节点和20这个右子节点的最长路径,显然9是一个叶子节点,只有本身一个节点;而20拥有自己的子节点,此时就需要算出从20出发的最长路径。

    20有左子节点15,右子节点7,这时需要继续计算15和7的最长路径,而15和7都是叶子节点,所以节点数只有1,再加上20节点本身这个属于根节点的子节点,20节点的最长路径就是2。

    同为3根节点的两个子节点9、20,子节点9的最长路径上节点数为1,子节点20的最长路径上节点数为2,取最大值后,再加上根节点自身的节点数,1+2=3,3就是最长路径上的节点数,也就是该二叉树的深度。

    分析到这里,我们知道了,要计算从根节点到最远叶子节点的节点个数,就需要先计算其左右子节点的最长路径,而要计算左右子节点的最长路径,就需要计算他们自身下面的左右子节点的最长路径了,对此我们可以使用递归,算完最下面叶子节点的个数后,再层层往上求其最大值。

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

    03 第二种解法

    既然可以使用递归,那我们也可以试着使用遍历的方法。从根节点开始,自顶向下遍历子节点,对此使用队列来临时存储每次遍历的子节点,遍历完一层子节点就加1,直到所有子节点都遍历完。

    public int maxDepth2(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int depth = 0;
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        while (!q.isEmpty()) {
            Queue<TreeNode> tem = new LinkedList<>();
            while(!q.isEmpty()){
                TreeNode node = q.poll();
                if (node.left != null) {
                    tem.offer(node.left);
                }
                if (node.right != null) {
                    tem.offer(node.right);
                }
            }
            q = tem;
            depth++;
        }
        return depth;
    }
    

    特殊情况还是和第一种解法一样,内层while循环是遍历每层的子节点。

    04 第三种解法

    第二种解法的内层循环那里,我们使用了新的队列来接收每次循环要进入的下一层节点数据,是否可以改动下,使其更加的简洁?这里我们使用队列的大小来控制它,从根节点开始,进入队列后,队列的size为1,开始进入内层循环,内层循环走了一次后,队列里又多了两个子节点,此时的size为2,然后继续开始内层循环,直到所有的子节点遍历完。

    public int maxDepth3(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int depth = 0;
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        while (!q.isEmpty()) {
            int n = q.size();
            while(n-- > 0){
                TreeNode node = q.poll();
                if (node.left != null) {
                    q.offer(node.left);
                }
                if (node.right != null) {
                    q.offer(node.right);
                }
            }
            depth++;
        }
        return depth;
    }
    

    05 验证与测试

    对于上面的两种解法,使用了一个简单的二叉树做了数据测试。

    public static void main(String[] args) {
        Easy_104_MaximumDepthOfBinaryTree instance = new Easy_104_MaximumDepthOfBinaryTree();
        TreeNode t = new TreeNode(1);
        TreeNode t2 = new TreeNode(2);
        TreeNode t3 = new TreeNode(3);
        TreeNode t4 = new TreeNode(4);
        TreeNode t5 = new TreeNode(5);
        TreeNode t6 = new TreeNode(6);
        TreeNode t7 = new TreeNode(7);
        TreeNode t8 = new TreeNode(8);
        t.left = t2;
        t.right = t3;
        t2.left = t4;
        t2.right = t5;
        t3.left = t6;
        t3.right = t7;
        t7.left = t8;
        long start = System.nanoTime();
        int result = instance.maxDepth(t);
        long end = System.nanoTime();
        System.out.println("maxDepth---输出:"+result+" , 用时:"+(end-start)/1000+"微秒");
        long start2 = System.nanoTime();
        int result2 = instance.maxDepth2(t);
        long end2 = System.nanoTime();
        System.out.println("maxDepth2---输出:"+result2+" , 用时:"+(end2-start2)/1000+"微秒");
        long start3 = System.nanoTime();
        int result3 = instance.maxDepth3(t);
        long end3 = System.nanoTime();
        System.out.println("maxDepth3---输出:"+result3+" , 用时:"+(end3-start3)/1000+"微秒");
    }
    

    测试结果如下:

    maxDepth---输出:4 , 用时:23微秒
    maxDepth2---输出:4 , 用时:586微秒
    maxDepth3---输出:4 , 用时:16微秒
    

    从测试的结果可以看出,遍历和递归都是可以解决问题的,第二种解法因为每进一次循环都要创建新的对象,这对内存和运行时间都是不小的消耗,经过优化后第三种解法更加适合遍历使用。

    06 小结

    以上就是全部内容,如果大家有什么好的解法思路、建议或者其他问题,可以下方留言交流,点赞、留言、转发就是对我最大的回报和支持!

  • 相关阅读:
    第15届创新英语大赛初赛第二阶段题目
    13.递归第一次
    13.递归第一次
    13.递归第一次
    13.递归第一次
    Algs4-1.3.19给出一段代码,删除链表的尾结点
    Algs4-1.3.18下面链表在代码的作用
    Algs4-1.3.17从文件中读取信息生成Transaction对象数组
    Algs4-1.3.16从文件中读取日期生成日期对象数组
    Algs4-1.3.15打印Queue中倒数第k个元素的值
  • 原文地址:https://www.cnblogs.com/xiaochuan94/p/9920433.html
Copyright © 2011-2022 走看看