代码随想录算法训练营第十六天 | 104.二叉树的最大深度、111. 二叉树的最小深度、222. 完全二叉树的节点个数

290 阅读5分钟

104.二叉树的最大深度

链接

题目链接

文章链接

第一想法

题目要求找出二叉树的最大深度,所以就是可以用层序法,最大深度就是层序法的层数,代码如下:

function maxDepth(root: TreeNode | null): number {
  let queue:TreeNode[]=[]
  let res:number=0
  if(root) queue.push(root)
  while(queue.length>0){
      let size:number=queue.length
      while(size--){
          let node:TreeNode=queue.shift()!
          if(node.left) queue.push(node.left)
          if(node.right) queue.push(node.right)
      }
      res++
  }
  return res
};

第二种方法就是递归法,通过比较每个叶子节点的深度来判断出最大深度:

function maxDepth(root: TreeNode | null): number {
  let res:number=0
  const dfs:(root: TreeNode | null,number:number)=>void=(root: TreeNode | null,number:number)=>{
      if(root==null){
      res=Math.max(res,number) 
      return 
      }
      //中其实就是下面的number+1
      dfs(root.left,number+1) //左
      dfs(root.right,number+1) //右
  }
  //这里从0开始是因为终止条件是默认加了1
  dfs(root,0)
  return res
};

看完文章后的想法

文章用了前序遍历,后序遍历和层序遍历,核心思想都是一样的,下面是后序遍历的代码:

function maxDepth(root: TreeNode | null): number {
  const dfs:(root: TreeNode | null)=>number=(root: TreeNode | null)=>{
      if(root==null) return 0;
      //中其实就是下面的number+1
      let left:number=dfs(root.left) //左
      let right:number=dfs(root.right) //右
      return 1+Math.max(left,right) //选择左右子树最大的深度返回
  }
  
  return dfs(root)
};

思考

这道题不难,我认为比较好理解的是层序遍历,但是前序遍历和后序遍历代码比较简洁,核心思想就是二叉树的最大深度就是根节点的最大高度。题(559. N 叉树的最大深度 - 力扣(Leetcode))也是相同的道理,下面展示不同的解法:

层序
function maxDepth(root: Node | null): number {
    let queue:(Node | null)[]=[]
    let number:number=0
    if(root) queue.push(root)
    while(queue.length>0){
        let size:number=queue.length
        while(size--){
            let node=queue.shift()!
            queue.push(...node.children)
        }
        number++
    }
    return number
};
前序
function maxDepth(root: Node | null): number {
    let res:number=0
    const dfs:(root: Node | null,number:number)=>void=(root: Node | null,number:number)=>{
        if(root==null){
            res=Math.max(number,res)
            return
        }
        //这个条件是用了进入终止条件的 因为用for的话 children=[]时不执行dfs 所以增加这个条件用于执行dfs
        if(root.children.length==0) dfs(null,number+1);
        for(let node of root.children){
           dfs(node,number+1)
        }
    }
    dfs(root,0)
    return res
};
后序
    function maxDepth(root: Node | null): number {
        let res:number=0
        const dfs:(root: Node | null)=>number=(root: Node | null)=>{
            if(!root) return 0 //这个是防止root=[]的情况
            if(root.children.length==0) return 1;
            let max:number=0
            for(let node of root.children){
              max=Math.max(max,dfs(node))
            }
            return max+1
        }
        return dfs(root)
    };

111. 二叉树的最小深度

链接

文章链接

题目链接

第一想法

最简单的还是利用层序遍历,当最先开始left和right都为null时就是最浅深度,代码如下:

function minDepth(root: TreeNode | null): number {
 let queue:TreeNode[]=[]
 let res:number=0
 if(root) queue.push(root)
 while(queue.length>0){
     let size:number=queue.length
     while(size--){
         let node:TreeNode=queue.shift()!
         if(node.left) queue.push(node.left)
         if(node.right) queue.push(node.right)
         if(!node.left&&!node.right) return res+1
     }
     res++
 }
 return 0
};

尝试用前序和后序遍历一下:

前序:

function minDepth(root: TreeNode | null): number {
    if(!root) return 0
    let min:number=Number.MAX_SAFE_INTEGER
  const dfs:(root:TreeNode | null,number:number)=>void=(root:TreeNode | null,number:number)=>{
    if(!root?.left&&!root?.right){
        min=Math.min(min,number)
        return
    }
    if(root.left) dfs(root.left,number+1)
    if(root.right)dfs(root.right,number+1)
  }
  dfs(root,1)
  return min
};

后序:

function minDepth(root: TreeNode | null): number {
    if(!root) return 0
  const dfs:(root:TreeNode | null)=>number=(root:TreeNode | null)=>{
    if(!root!.left&&!root!.right) return 1;
    let left:number=Number.MAX_SAFE_INTEGER
    let right:number=Number.MAX_SAFE_INTEGER
    if(root!.left) left=dfs(root!.left)
    if(root!.right) right=dfs(root!.right)
   return 1+Math.min(left,right)
  }
  return dfs(root)
};

看完文章后的想法

代码随想录中也用了前序遍历、后序遍历以及层序遍历,其中前序遍历和层序遍历是一样的,但是后序遍历是有所不同的,我的解法中是用root.left和root.right都不存在时才是子节点,也就是这个条件if(!root!.left&&!root!.right) return 1;,下面给出代码随想录中的解法:

function minDepth(root: TreeNode | null): number {
    if (root === null) return 0;
    if (root.left !== null && root.right === null) { //右节点存在而左节点不存在,所以最小深度为 1 + minDepth(root.left);
        return 1 + minDepth(root.left);
    }
    if (root.left === null && root.right !== null) {
        return 1 + minDepth(root.right);
    }
    //左右节点都存在
    return 1 + Math.min(minDepth(root.left), minDepth(root.right));
}

思考

这道题也不难,但是其中有一点比较容易踩坑,就是不能按照二叉树的最大深度来计算,因为最大深度是求最后一层的节点,肯定是是叶子节点,但是求最浅深度,当一个节点有左孩子但是没有右孩子,而它不是叶子节点,就是代码随想录中的这张图:

image.png

222. 完全二叉树的节点个数

链接

文章链接

题目链接

第一想法

还是最简单的利用层序遍历,代码如下:

    function countNodes(root: TreeNode | null): number {
        let queue:(TreeNode | null)[]=[]
        let res:number=0
        if(root) queue.push(root)
        while(queue.length>0){
            let node=queue.shift()!
            if(node.left) queue.push(node.left)
            if(node.right) queue.push(node.right)
            res++
        }
        return res
      };

递归法也是可以的,这里我用的是前序遍历:

    function countNodes(root: TreeNode | null): number {
        let res:number=0
      const dfs:(root: TreeNode | null)=>void=(root: TreeNode | null)=>{
        if(root==null) return;
        res++
        dfs(root.left)
        dfs(root.right)
      }
      dfs(root)
      return res
    };

看完文章后的想法

我所用的方法都是都是可以解决普通二叉树的方法,但是文章用了一种方法,他的核心思想就是利用完全二叉树的性质,满足完全二叉树,则递归它的左右孩子,最终一定会出现满二叉树,这是代码随想录中的图片:

image.png

image.png 所以递归方法为如果左右子树的深度都相同,那么就是一棵满二叉树,如图:

image.png 这种情况就不是满二叉树: image.png 代码如下:

function countNodes(root: TreeNode | null): number {
  const dfs:(root: TreeNode | null)=>number=(root: TreeNode | null)=>{
    if(root==null) return 0;
    let left:number=0
    let right:number=0
    let curr:TreeNode|null=root
    while(curr){
        left++
        curr=curr.left
    }
    curr=root
    while(curr){
        right++
        curr=curr.right
    }
   if(left===right) return 2**left-1 //如果是满二叉树则返回节点个数
   return 1+dfs(root.left)+dfs(root.right) //不满足满二叉树就往下继续递归,返回左节点个数+右节点个数+当前节点
  }
  return dfs(root)
};

思考

这道题如果按照一般二叉树来写是比较容易写出来的,但是利用完全二叉树的性质来计算是没想到的,其中一个核心就是完全二叉树的左右节点的终止一定满足满二叉树,上文中已经提到。所以这道题要学习利用完全二叉树来解决这个问题。

总结

今天一共四道题,耗时2.5小时,其中没想到以及需要学习的就是最后一道题(222题)学习利用完全二叉树的性质来解决这道题