前端刷题路-Day12|刷题打卡

424 阅读2分钟

掘金团队号上线,助你 Offer 临门! 点击 查看详情

二叉树的层序遍历(题号102)

题目

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。

示例:

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

    3

   / \
  9  20
    /  \
   15   7

返回其层序遍历结果:

[
  [3],
  [9,20],
  [15,7]
]

链接

leetcode-cn.com/problems/bi…

解释

这题其实感觉比较简单,就是一层层的循环跟节点,逻辑上没有什么需要注意的,就是代码的书写上有一些需要注意的问题,看看👇的代码就知道了。

自己的答案

var levelOrder = function(root) {
  if (!root) return []
  var arr1 = [root]
      arr2 = []
  while (arr1.length > 0) {
    const { val, root } = arr1.reduce((total, item) => {
      total.val.push(item.val)
      item.left && total.root.push(item.left)
      item.right && total.root.push(item.right)
      return total
    }, { val: [], root: [] })
    arr1 = root
    arr2.push(val)
  }
  return arr2
};

arr1用来存储节点,arr2用来存储最后的值。

首选处理root不存在的情况,这一点毋庸置疑。

之后开始遍历arr1,如果它存在,那么就循环它里面的内容,循环时要做两件事,首先拿到当前节点的值,之后拿到它的左节点和右节点,循环完毕后分别放到arr1arr2中即可。

一开始用的是map,后来想了想,用了reduce,代码看上去确实更好一些,但感觉没有本质上的改变,依然需要两变量来存储每次循环后拿到的值,此时就有值得提升的地方了。

更好的方法

var levelOrder = function(root) {
  if (!root) return []
  var arr1 = [root]
      arr2 = []
  while (arr1.length > 0) {
    var arrL = arr1.length
    arr2.push([])
    for (let i = 0; i < arrL; i++) {
      const node = arr1.shift();
      arr2[arr2.length - 1].push(node.val)
      node.left && arr1.push(node.left)
      node.right && arr1.push(node.right)
    }
  }
  return arr2
};

这里相对于我自己的答案,并没有新建两个变量来存储节点和值,它其实利用了while的特性,我即使在内部修改arr1,但是在本次while循环结束前,其内部的arr1是不会受到影响的,那么就可以放心得对arr1arr2进行处理了。

首先拿到循环前的arr1的长度,之后一个for循环,此时可以判断应该从arr1拿出几个节点,也不会拿太多,拿到下一轮循环的节点。

处理arr2是就更加简单了,在for循环开始前,先往arr2中插入一个空数组,之后在for循环中直接往arr2的最后一个元素中插入值即可,也不用新建一个变量来存储for循环中拿到的val来。

简单来说就是这样了,别的都没啥,就是细节都处理上不够到位,有待提高。

二叉树的最大深度(题号104)

题目

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

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

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

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

   3
   / \
  9  20
    /  \
   15   7

返回它的最大深度 3 。

链接

leetcode-cn.com/problems/ma…

解释

这题主要有两种方法来解答,一种是广度优先算法(Breadth-First-Search),一种是深度优先算法(Depth-First-Search)

自己的答案(广度优先算法)

var maxDepth = function(root) {
  if (!root) return 0
  var level = 0
      nodes = [root]
  while (nodes.length > 0) {
    var nL = nodes.length
    for (let i = 0; i < nL; i++) {
      var node = nodes.shift()
      node.left && nodes.push(node.left)
      node.right && nodes.push(node.right)
      if (i === nL - 1) level++
    }
  }
  return level
};

这没啥可说的 ,上一题十分类似,横向遍历所有节点,然后累计遍历层数

自己的答案(深度优先算法)

var maxDepth = function (node, length = 0) {
  if (node) {
    var left = maxDepth(node.left, length + 1)
    right = maxDepth(node.right, length + 1)
    return Math.max(left, right) || 1 + length
  } else {
    return 0
  }
};

这方法看上去不错,而且是纯自己想出来的,没有参考别的资料什么的。不过一如既往,还是有些小瑕疵的,比方说这一行:

return Math.max(left, right) || 1 + length

这里感觉是有些奇怪的,本来是这样的:

return Math.max(1, left, right) + length

可是怎么算都不对,之后无意间尝试了上面的一种写法,然后就对了,之后就很莫名其妙,为什么会对呢?原来上面的写法将整体的运算逻辑改变了,如果Math.max是0,就直接返回1 + length,这样整体逻辑就对了,

好好想想,如果还是不明白的话。

更好的答案(深度优先算法)

const maxDepth = (root) => {
  if (root == null) return 0;
  const leftMaxDepth = maxDepth(root.left);
  const rightMaxDepth = maxDepth(root.right);
  return 1 + Math.max(leftMaxDepth, rightMaxDepth);
};

毕竟是第一次写深度优先算法,看了看别人的,发现自己还是有提升空间的,首先来说可以将root不存在的情况提出来,后续就是根本不需要第二个length参数,只需要每次累计1即可,然后进行递归调用,这其实和求平方那一题有点像,完全不要一个length来进行中转,递归的过程中并不需要这个数。

所以说以后写代码不要着急,仔细想想在动笔,会清晰很多的。



PS:想查看往期文章和题目可以点击下面的链接:

这里是按照日期分类的👇

前端刷题路-目录(日期分类)

经过有些朋友的提醒,感觉也应该按照题型分类
这里是按照题型分类的👇

前端刷题路-目录(题型分类)

有兴趣的也可以看看我的个人主页👇

Here is RZ