代码随想录算法训练营第十五天|二叉树part2

68 阅读5分钟

104.二叉树的最大深度

题目链接:104. 二叉树的最大深度 - 力扣(LeetCode)

思路

  1. 递归法:

    这里可以使用前序(中左右),也可以使用后序遍历(左右中),最后不要使用中序来,原理可以见翻转二叉树这一节

    这里先用后序遍历(左右中),递归三要素如下:

    • 确定递归函数的参数和返回值 —— root,深度
    • 确定终止条件 —— 如果是空节点,则什么没有子节点,返回0
    • 确定单层递归逻辑: 先求左子树深度,再求右子树深度,取最大值再加一

    JS代码如下:

    function getDepth(node){
         if(node == null) return 0;
         let leftDepth = getDepth(node.left);
         let rightDepth = getDepth(node.right);
         let depth = Math.max(leftDepth,rightDepth) + 1;
         return depth;
     }
    var maxDepth = function(root) {
        return getDepth(root);
    };
    

    前序遍历也可以完成这道题

    function getDepth(node, depth, result) {
      result.val = Math.max(result.val, depth);
      if (!node.left && !node.right) {
        return;
      }
      if (node.left) {
        getDepth(node.left, depth + 1, result);
      }
      if (node.right) {
        getDepth(node.right, depth + 1, result);
      }
    }
    ​
    function maxDepth(root) {
      if (!root) {
        return 0;
      }
      let result = { val: 0 };
      getDepth(root, 1, result);
      return result.val;
    }
    

    JS中没有指针,所以用对象的值来保存result

    这里是精简了代码,实际上单层递归逻辑是这样的:

     if (node.left) {
         // 有子树就深度加一
        depth++;
         // 再往下扩展
        getDepth(node.left, depth, result);
         // 扩展到最下边就回溯上来,再去找有右子树的节点
         depth--;
      }
      if (node.right) {
        depth++;
        getDepth(node.right, depth, result);
         depth--;
      }
    return;
    
  2. 层序遍历法

    稍微修改一下模板代码即可:

    var maxDepth = function(root) {
        //二叉树的层序遍历
        let queue = [],depth = 0;
        queue.push(root);
        if(root === null) {
            return depth;
        }
        while(queue.length !== 0) {
            // 记录当前层级节点数,方便之后以此弹出
            let length = queue.length;
            depth++;
            for(let i = 0;i < length; i++) {
                let node = queue.shift();
                // 存放当前弹出的节点
                // 存放当前层下一层的节点
                node.left && queue.push(node.left);
                node.right && queue.push(node.right);
            }
            //把每一层的结果放到结果数组
            
        }   
        return depth;
    ​
    };
    

总结

这一题既可以用深度优先的方法,又可以用广度优先的方法来做

深度优先即递归法:

后序遍历的模式:

对于每一个子节点,只需要求其左子树和右子树的深度并取最大值即可,就这样层层递归下去

前序遍历的模式:

判断逻辑需要放在最前面,所以设置一个result值用来保存每次执行后的depth结果的最大值

然后左右子树层层递归,相对来说这种算法才是一般的求深度的逻辑

后序遍历模式简单一些,这种有点类似于面向对象的思想,我只需要对每一个节点做比较左右子树深度的操作,然后让他们层层传下去即可。前序更像是面向业务逻辑,直接把全部的想清楚。

层序遍历模式:

这个还是套模板比较容易

在每遍历完一层depth就加1

比较简单

559.n叉树的最大深度

题目链接:559. N 叉树的最大深度 - 力扣(LeetCode)

思路

递归法:

var maxDepth = function(root) {
    if(root == null){
        return 0;
    }
    let depth = 0;
    for(let i of root.children){
        depth = Math.max(depth,maxDepth(i));
    }
     return depth+1;  
};

注意递归的逻辑

相比于上面二叉树的递归,逻辑被简化了

迭代法:

var maxDepth = function(root) {
    //二叉树的层序遍历
    let queue = [],depth = 0;
    queue.push(root);
    if(root === null) {
        return depth;
    }
    while(queue.length !== 0) {
        // 记录当前层级节点数,方便之后以此弹出
        let length = queue.length;
        depth++;
        for(let i = 0;i < length; i++) {
            let node = queue.shift();
            // 存放当前弹出的节点
            // 存放当前层下一层的节点
            for(let i of node.children){
                queue.push(i)
            }
        }
        //把每一层的结果放到结果数组        
    }   
    return depth;
};

总结

这里n叉数的逻辑和二叉树大致相似

对于子节点的遍历,可以用for.... of来完成

111.二叉树的最小深度

题目链接:111. 二叉树的最小深度 - 力扣(LeetCode)

第一想法

还是使用递归的后序遍历

这里并不是最大深度中把max()换成min()就好了

因为题目说了是根节点到叶子节点的最小距离

所以根节点的下一层没有节点的情况需要单独拿出来考虑

这样还是不行,因为存在这种情况

image-20230403140100966

思路

实际上应该在单层递归逻辑地方做一个判断

如果左子树为空,右不为空则返回右边的深度

如果右空左不空则返回左边的深度

最后取一个最小值

function getDepth(node){
     if(node == null) return 0;
     let leftDepth = getDepth(node.left);
     let rightDepth = getDepth(node.right)
     if(node.left == null & node.right !== null){
         return rightDepth + 1;
     }else if(node.right == null && node.left !== null){
         return leftDepth + 1;
     }else{
         result = Math.min(leftDepth,rightDepth) + 1;
         return result;
     }
 }
var minDepth = function(root) {
    return getDepth(root);
};

迭代法:

var minDepth = function(root) {
     //二叉树的层序遍历
    let queue = [],depth = 0;
    queue.push(root);
    if(root === null) {
        return depth;
    }
    while(queue.length !== 0) {
        // 记录当前层级节点数,方便之后以此弹出
        let length = queue.length;
        depth++;
        for(let i = 0;i < length; i++) {
            let node = queue.shift();
            // 存放当前弹出的节点
            // 存放当前层下一层的节点
            if(!node.left && !node.right) return depth;
            node.left && queue.push(node.left);
            node.right && queue.push(node.right);
            }
        }     
      
    return depth;
};

这里的逻辑和二叉树最大深度的逻辑很相似,不同的地方在于:

if(!node.left && !node.right) return depth;

即,如果碰到没有子节点的节点,就立刻返回depth,这样可以得到最小的深度

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

题目链接:222. 完全二叉树的节点个数 - 力扣(LeetCode)

第一想法

设置一个变量存储节点数量,层序遍历二叉树,每次有节点出队列,变量就加一

思路

递归法:

  • 确定递归函数的参数和返回值

    参数就是节点,返回值是节点的个数

  • 确定终止条件

    如果是空节点,则返回

  • 确定单层递归逻辑

    先求左边树节点数量,再求右边树节点数量,最后相加再+1

function count(node){
     if(node == null){
         return 0;
     }
     let left = count(node.left);
     let right = count(node.right);
     let sum = left + right + 1;
     return sum;
 }
var countNodes = function(root) {
    return count(root);
};

迭代法:

var countNodes = function(root) {
    let queue = [];
    let num = 0;
    if(root == null) return 0;
    queue.push(root);
    while(queue.length){
        let len = queue.length;
        for(let i = 0; i < queue.length; i++){
            let node = queue.shift();
            
            num++;
            node.left && queue.push(node.left);
            node.right && queue.push(node.right);
        }
    }
    return num;
}

这一题还可以利用完全二叉树的性质,即要么是满二叉树,要么可以层层遍历下去,最后的子树一定是满二叉树

可以用递归来做:

var countNodes = function(root) {
    //利用完全二叉树的特点
    if(root === null) {
        return 0;
    }
    let left = root.left;
    let right = root.right;
    let leftDepth = 0, rightDepth = 0;
    while(left) {
        left = left.left;
        leftDepth++;
    }
    while(right) {
        right = right.right;
        rightDepth++;
    }
    if(leftDepth == rightDepth) {
        return Math.pow(2, leftDepth+1) - 1;
    }
    return countNodes(root.left) + countNodes(root.right) + 1;
};