问题:104.二叉树的最大深度

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

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

【二叉树 2.2】二叉树的最大深度和最小深度_二叉树

返回它的最大深度 3 。

递归法

递归三步走

本题其实也要后序遍历(左右中),依然是因为要通过递归函数的返回值做计算树的高度。

按照递归三部曲,来看看如何来写。

确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回这棵树的深度,所以返回值为int类型。
代码如下:

int getDepth(TreeNode* node)

确定终止条件:如果为空节点的话,就返回0,表示高度为0。
代码如下:

if (node == NULL) return 0;

确定单层递归的逻辑:先求它的左子树的深度,再求的右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。
代码如下:(后序遍历,按照左右中的顺序)

int leftDepth = getDepth(node->left); // 左
int rightDepth = getDepth(node->right); // 右
int depth = 1 + max(leftDepth, rightDepth); // 中 每次更新
return depth;

递归算法(后序遍历)

所以整体代码如下:

class Solution {
    public int maxDepth(TreeNode root) {
      if (null == root) return 0;
      int leftDepth = maxDepth(root.left);  
      int rightDepth = maxDepth(root.right);
      int depth = 1+Math.max(leftDepth,rightDepth);  // 后序遍历,左右中,每次中的时候操作计算出深度
      return depth;
    }
}

这里我打算用统一模板的后序遍历的迭代写法再写一次,一层while循环,但是不好写,但是还是写出来了

迭代法(后序迭代)

class Solution {
    public int maxDepth(TreeNode root) {
      if (null == root) return 0;  // null==root判断要在三个之前,防止空指针   return;返回一个完成标志就好了
        List<Integer> list=new ArrayList<>();  // 返回值为list,但是参数中没有list,所以必须新建一个list盛放返回值
        Stack<TreeNode> stack=new Stack<>();
        stack.push(root);
        int depth =0;
        int result =0;
        while (!stack.isEmpty()){
            TreeNode node = stack.peek(); //
            if (node!=null){
                stack.pop();
                stack.push(node);stack.push(null);depth++;
                if (node.right !=null)stack.push(node.right);  // 这一条if保证插入的不是一个null,后面取出node.val的时候不是空指针异常
                if (node.left !=null) stack.push(node.left);
            }else{
                stack.pop();
                node = stack.pop();
                depth--;
            }
            result = Math.max(result,depth);  // 取最大值
        }
        return result;
    }
}

迭代法(这种一层一层计算高度,最好用层序遍历)

使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。

所以这道题的迭代法就是一道模板题,可以使用二叉树层序遍历的模板来解决的,代码如下:

class Solution {
    public int maxDepth(TreeNode root) {
      if (null == root) return 0;
      Queue<TreeNode> queue=new LinkedList<>();
      int depth=0;  // 这里之所以赋值为0是因为虽然queue.offer(root),但是一定可以进去while循环中,执行一次depth++,所以即使只有一个根节点,也可以保证return depth;是return 1;
      queue.offer(root);
      while (!queue.isEmpty()){
          int size = queue.size();
         depth++; // 每
          for (int i=0;i<size;i++){
             // 层序遍历模板,两层循环,内层循环中,下一次pop的就是上一次push的元素
             TreeNode node = queue.poll();  // 最上面的节点
             if (node.left != null) queue.offer(node.left);
             if (node.right != null) queue.offer(node.right);
         }
      }
      return depth;
    }
}

这里之所以赋值为0是因为虽然queue.offer(root),但是一定可以进去while循环中,执行一次depth++,所以即使只有一个根节点,也可以保证return depth;是return 1;

那么我们可以顺便解决一下N叉树的最大深度问题

问题:559.N叉树的最大深度

给定一个 N 叉树,找到其最大深度。

最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。

例如,给定一个 3叉树 :

图片

我们应返回其最大深度,3。

思路
依然可以提供递归法和迭代法,来解决这个问题,思路是和二叉树思路一样的,直接给出代码如下:

递归法,后序遍历

class Solution {
    public int maxDepth(Node root) {
        if (null == root) return 0;
        int max=0;
        for (int i=0;i<root.children.size();i++){
            int depth = maxDepth(root.children.get(i));
            if (depth > max)
                max=depth;   // 取出最大值
        }
        return max+1;
    }
}

变得更加简单,如下:

class Solution {
    public int maxDepth(Node root) {
        if (null == root) return 0;
        int depth=0;
        for (int i=0;i<root.children.size();i++){
            depth = Math.max(maxDepth(root.children.get(i)),depth);
        }
        return depth+1;
    }
}

迭代法(后序遍历)

class Solution {
    public int maxDepth(Node root) {
        if (null == root) return 0;  // null==root判断要在三个之前,防止空指针   return;返回一个完成标志就好了
        List<Integer> list=new ArrayList<>();  // 返回值为list,但是参数中没有list,所以必须新建一个list盛放返回值
        Stack<Node> stack=new Stack<>();
        stack.push(root);
        int depth =0;
        int result =0;
        while (!stack.isEmpty()){
            Node node = stack.peek(); //
            if (node!=null){
                stack.pop();
                stack.push(node);stack.push(null);depth++;
                for (int i=0;i<node.children.size();i++){
                    if (node.children.get(i)!=null)    // 这一条if保证插入的不是一个null,后面取出node.val的时候不是空指针异常
                        stack.push(node.children.get(i));
                }
            }else{
                stack.pop();
                node = stack.pop();
                depth--;
            }
            result = Math.max(result,depth);  // 取最大值
        }
        return result;
    }
}

这里面depth++ depth-- 有重复计算的

迭代法,层序遍历

依然是层序遍历,代码如下:

class Solution {
    public int maxDepth(Node root) {
      if (null == root) return 0;
      Queue<Node> queue=new LinkedList<>();
      queue.offer(root);
      int depth=0;  // 这里之所以赋值为0是因为虽然queue.offer(root),但是一定可以进去while循环中,执行一次depth++,所以即使只有一个根节点,也可以保证return depth;是return 1;
      while (!queue.isEmpty()){
          int size = queue.size();
          depth++; // 每
          for (int i=0;i<size;i++){
             Node node = queue.poll();  // 最上面的节点
              for (int j=0;j<node.children.size();j++){
                  if (node.children.get(j)!=null) queue.offer(node.children.get(j));
              }
         }
      }
      return depth;
    }
}

这里之所以赋值为0是因为虽然queue.offer(root),但是一定可以进去while循环中,执行一次depth++,所以即使只有一个根节点,也可以保证return depth;是return 1;

问题:111.二叉树的最小深度

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

说明: 叶子节点是指没有子节点的节点。

示例:

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

【二叉树 2.2】二叉树的最大深度和最小深度_二叉树_02

返回它的最小深度 2.

思路

看完了这篇二叉树:看看这些树的最大深度,再来看看如何求最小深度。直觉上好像和求最大深度差不多,其实还是差不少的。遍历顺序上依然是后序遍历(因为要比较递归返回之后的结果),但在处理中间节点的逻辑上,最大深度很容易理解,最小深度可有一个误区,如图:

【二叉树 2.2】二叉树的最大深度和最小深度_二叉树_03

这就重新审题了,题目中说的是:「最小深度是从根节点到最近叶子节点的最短路径上的节点数量。」,注意是「叶子节点」。

什么是叶子节点,左右孩子都为空的节点才是叶子节点!

递归法

递归三部曲

第一步,确定递归函数的参数和返回值

参数为要传入的二叉树根节点,返回的是int类型的深度。

代码如下:

int minDepth(TreeNode root)

第二步,确定终止条件
终止条件也是遇到空节点返回0,表示当前节点的高度为0。

代码如下:

if (null == root) return 0;

第三步,确定单层递归的逻辑
这块和求最大深度可就不一样了,一些同学可能会写如下代码:

int leftDepth = minDepth(root.left);
int rightDepth = minDepth(root.right);
int depth = 1+ Math.min(leftDepth,rightDepth);
return depth;

这个代码就犯了此图中的误区:

【二叉树 2.2】二叉树的最大深度和最小深度_# (4)二叉树_04

如果这么求的话,没有左孩子的分支会算为最短深度。

所以,

  1. 如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。
  2. 右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。
  3. 如果左右子树都不为空,返回左右子树深度最小值 + 1 。

代码如下:

int leftDepth = minDepth(root.left);
int rightDepth = minDepth(root.right);
if (root.left==null && root.right!=null)
    return 1+rightDepth;
if (root.left!=null && root.right==null)
    return 1+leftDepth;
int depth = 1+ Math.min(leftDepth,rightDepth);
return depth;

遍历的顺序为后序(左右中),可以看出:「求二叉树的最小深度和求二叉树的最大深度的差别主要在于处理左右孩子不为空的逻辑,就是多添加了几个if判断」

递归算法,后序遍历

整体递归代码如下:

class Solution {
    public int minDepth(TreeNode root) {
        if (null == root) return 0;
        // 左
        int leftDepth = minDepth(root.left);
        // 右
        int rightDepth = minDepth(root.right);
        // 中
        if (root.left==null && root.right!=null)
            return 1+rightDepth;
        if (root.left!=null && root.right==null)
            return 1+leftDepth;
        int depth = 1+ Math.min(leftDepth,rightDepth);
        return depth;
    }
}

迭代法,层序遍历

相对于二叉树:看看这些树的最大深度,本题还可以使用层序遍历的方式来解决,思路是一样的。

如果对层序遍历还不清楚的话,可以看这篇:二叉树:层序遍历登场!

「需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点」

代码如下:

class Solution {
    public int minDepth(TreeNode root) {
        if (null == root) return 0;
        Queue<TreeNode> queue = new LinkedList();
        int depth=0;   // 这里之所以赋值为0是因为虽然queue.offer(root),但是一定可以进去while循环中,执行一次depth++,所以即使只有一个根节点,也可以保证return depth;是return 1;
        queue.offer(root);
        while (!queue.isEmpty()){
           int size=queue.size();
           depth++;
           int flag=0;
           for (int i=0;i<size;i++){
               TreeNode node = queue.poll();
               if (node.left!=null) queue.offer(node.left);
               if (node.right!=null) queue.offer(node.right);
               if (node.left==null && node.right==null){  
                   flag=1;
                   break;
               }
           }
           if (flag==1)
               break;
        }
        return depth;
    }
}

这里之所以赋值为0是因为虽然queue.offer(root),但是一定可以进去while循环中,执行一次depth++,所以即使只有一个根节点,也可以保证return depth;是return 1;