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 小结

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

posted @ 2018-11-07 08:19  程序员小川  阅读(248)  评论(0编辑  收藏  举报