【代码随想录 | day16】(JavaScript) 二叉树系列:层序遍历及相关题目(下)

159 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情

  • 429.N叉树的层序遍历
  • 515.在每个树行中找最大值
  • 116.填充每个节点的下一个右侧节点指针
  • 二叉树的最大深度
  • 二叉树的最小深度

429.N叉树的层序遍历

题目链接:N 叉树的层序遍历

给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。

树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。

思路:由于这是N叉树,所以一个节点下面不一定有两个子节点。那么这题就不能用node.leftnode.right。题目中已经给定了类 Node ,其中含有属性:val 、 children 。

代码

 var levelOrder = function (root) {
   let res = [], queue = [];
   queue.push(root);
   while (queue.length && root !== null) {
     // 记录每一层节点个数
     let length = queue.length;
     // 存放每层节点,也和二叉树一致
     let curLevel = [];
     while (length--) {
       let node = queue.shift();
       curLevel.push(node.val);
       // 这里不再是左右子节点了,而是循环node.children
       for (let item of node.children) {
         item && queue.push(item);
       }
     }
     res.push(curLevel);
   }
   return res;
 };
  • while (length--) { } 换成 for (let i = 0; i < length; i++) { } 也可以运行

515.在每个树行中找最大值

题目链接:在每个树行中找最大值

思路以及代码

方法一:还是使用层序遍历的代码,之前是每层遍历完的结果是放到curLevel里面,然后再放到结果里。现在是直接找出数组curLevel里面的最大值,然后放到结果里面。

 var largestValues = function (root) {
   let res = [], queue = [];
   queue.push(root);
   while (queue.length && root !== null) {
     let length = queue.length;
     let curLevel = [];
     while (length--) {
       let node = queue.shift()
       curLevel.push(node.val);
       node.left && queue.push(node.left);
       node.right && queue.push(node.right);
     }
     let max = Math.max(...curLevel);
     res.push(max)
   }
   return res;
 };

方法二:将max的初始值设为队列的第一个元素,然后再遍历后续元素的时候,不断比较和更新max的数值。

 var largestValues = function (root) {
   //使用层序遍历
   let res = [], queue = [];
   queue.push(root);
   while (root !== null && queue.length) {
     //设置max初始值就是队列的第一个元素
     let max = queue[0].val;
     let length = queue.length;
     while (length--) {
       let node = queue.shift();
       max = max > node.val ? max : node.val;
       node.left && queue.push(node.left);
       node.right && queue.push(node.right);
     }
     //把每一层的最大值放到res数组
     res.push(max);
   }
   return res;
 };

116.填充每个节点的下一个右侧节点指针

题目链接:填充每个节点的下一个右侧节点指针

116.填充每个节点的下一个右侧节点指针

思路以及代码

由于本题不需要得到一定结果集,所以这里也不用定义res。只不过在单层遍历的时候记录一下本层的头部节点,然后在遍历的时候让前一个节点指向本节点就可以了

 var connect = function(root) {
     if (root === null) return root;
     let queue = [root];
     while (queue.length) {
         let n = queue.length;
         for (let i=0; i<n; i++) {
             let node = queue.shift();
             if (i < n-1) {
                 node.next = queue[0];
             }
             node.left && queue.push(node.left);
             node.right && queue.push(node.right);
         }
     }
     return root;
 };
  • if (i < n-1) { node.next = queue[0]; } 在层序遍历那一题中,会用curLevel.push(node.val)存入每层的节点。但是现在用代码中的 if 语句,就能实现让前一个节点指向本节点了。

104.二叉树的最大深度

题目链接:二叉树的最大深度

对于二叉树中的某个节点,其深度是从根节点到该节点的最长简单路径所包含的节点个数,是从上面向下面数的。

思路及代码

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

 var maxDepth = function (root) {
   // 最大的深度就是二叉树的层数
   if (root === null) return 0;
   let queue = [root];
   let height = 0;
   while (queue.length) {
     let n = queue.length;
     height++;
     for (let i = 0; i < n; i++) {
       let node = queue.shift();
       node.left && queue.push(node.left);
       node.right && queue.push(node.right);
     }
   }
   return height;
 };

方法二:使用递归法

借用参考文章里面大佬的分析:

  1. 递归函数的返回值和参数:返回值为当前节点的最大深度,参数为当前节点
  2. 递归函数的单层逻辑:当前节点的最大深度 = 1 + 左子树或右子树的最大深度
  3. 递归函数的终止条件:当前节点为空时,当前节点最大深度为0
 var maxDepth = function (root) {
   // 递归终止条件
   if (root === null) return 0;
 ​
   // 单层递归逻辑
   let leftDepth = maxDepth(root.left);
   let rightDepth = maxDepth(root.right);
 ​
   
   return 1 + Math.max(leftDepth, rightDepth);
 };

111.二叉树的最小深度

题目链接:二叉树的最小深度

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

思路及代码

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

 var minDepth = function (root) {
   if (root === null) return 0;
   let queue = [root];
   let depth = 0;
   while (queue.length) {
     let n = queue.length;
     depth++;
     for (let i = 0; i < n; i++) {
       let node = queue.shift();
       // 如果左右节点都是null(在遇见的第一个leaf节点上),则该节点深度最小
       if (node.left === null && node.right === null) {
         return depth;
       }
       node.left && queue.push(node.left);;
       node.right && queue.push(node.right);
     }
   }
   return depth;
 };

方法二:递归法。

 var minDepth = function(root) {
   // 递归终止条件
   if (root === null) return 0;
 ​
   // 单层递归逻辑
   let leftDepth = minDepth(root.left);
   let rightDepth = minDepth(root.right);
   // 单个子树为空的处理
   if (root.left && !root.right) return 1 + leftDepth;
   if (!root.left && root.right) return 1 + rightDepth;
   // 两个子树均不为空的处理
   return 1 + Math.min(leftDepth, rightDepth);
 };

参考文章