leetcode之广度优先遍历(二叉树)

266 阅读2分钟

广度优先遍历算法考察频度很高!

广度优先遍历(bfs)需要借助队列来实现,因为队列先进先出、符合一层一层遍历的逻辑。同理栈先进后出的特性,适合用来模拟深度优先遍历也就是递归的逻辑。

「二叉树层序遍历思路」

  • 如果是空树,直接return []

  • 首先根元素入队,

  • 当队列不为空时(while循环)

    • 记录当前队列的长度len(当前层级的节点数)
    • 一次性遍历完当前层的len个节点,并拓展操作
    • 最后将这层的结果放到结果数组。

注意:

  1. 队列先进先出要用shiftpush表示。
  2. arr.push(cur)中的参数cur如果是数组,那么就会把一整个数组'[]'推到arr中。
const arr = [1, 2];
const cur = [3, 4];
arr.push(cur);
console.log(arr);  //  [ 1, 2, [ 3, 4 ] ]

102. 二叉树的层序遍历

题目102.二叉树的层序遍历

注意,题目要求的结果形式是 [[1], [2, 3], [4, 5, 6]],二维数组形式。

const levelOrder = function(root) {
  if (!root)  return [];
  
  const res = [];  // 结果数组
  const queue = [root];  // 初始化队列
  
  while (queue.length > 0) {
    let len = queue.length;  // 记录当前层级的节点数
    const currLevel = [];  // 存放当前层级的节点值
    // 一次性处理完当前层级的所有节点
    for (let i = 0; i < len; i++) {
      let node = queue.shift();
      currLevel.push(node.val);
      node.left && queue.push(node.left);  // 注意
      node.right && queue.push(node.right);  // 注意
    }  
    res.push(currLevel);
  }
  return res;
};

时间复杂度:O(n)。 因为每个节点都进队出队各一次。

空间复杂度:O(n)。 队列中元素的个数不超过n个。

拓展一:107. 二叉树的层序遍历II

题目107. 二叉树的层序遍历II

只需要在102.二叉树的层序遍历基础上,最后把结果数组res反转一下就可以了。

注意,reverse()会改变原数组。

return res.reverse();

注意,对于多维数组,reverse()只会反转第一层,不会深入反转。

const arr = [ 1, 2, [ 3, 4 ] ];
console.log(arr.reverse());   // [ [ 3, 4 ], 2, 1 ]

拓展二:429. N叉树的层序遍历

题目429.N叉树的层序遍历

N叉树和二叉树的区别在于N叉树的子节点不止左右两个,所以不能再使用node.leftnode.right表示,而是循环node.children

for (let i = 0; i < len; i++) {
  let node = queue.shift();
  curLevel.push(node.val);
  for (let item of node.children) {  // 唯一的改变
    queue.push(item); 
  }

拓展三:103. 二叉树的锯齿形层序遍历

题目103.二叉树的锯齿形层序遍历

锯齿形层次遍历就是,先从左往后,下一层再从右往左,如此反复进行。

通过flag实现。

const levelOrder = function(root) {
  ...
  let flag = false;
  while(...){
      ...
      for () {}
      res.push(flag ? currLevel.reverse() : currLevel);
      flag = !flag;
  }
  return res;
};

时间复杂度:O(n)。

空间复杂度:O(n)。

拓展四:199. 二叉树的右视图

题目199. 二叉树的右视图

层序遍历的时候,判断是否遍历到当前层级的最后一个元素,是就放进res数组。

因为每个层级只取一个元素,所以不需要curLevel存放节点,直接res.push(val)即可。

const levelOrder = function(root) {
  if (!root)  return [];
  const res = [];  
  const queue = [root];  
  
  while (queue.length > 0) {
    let len = queue.length;  
    for (let i = 0; i < len; i++) {
      let node = queue.shift();
      if (i === len - 1) {  // 注意不同
        res.push(node.val);
      }
      node.left && queue.push(node.left);
      node.right && queue.push(node.right);
    }
  }
  return res;
};

拓展五:剑指32-I.从上到下打印二叉树

题目32-I.从上到下打印二叉树

这题的思路和199.二叉树的右视图类似,也是不用curLevel存放当前层级节点,res直接存放遍历的所有节点即可。

const levelOrder = function(root) {
  if (!root)  return [];
  const res = [];  // 结果数组
  const queue = [root];  // 队列
  
  while (queue.length > 0) {
    let len = queue.length;  
    for (let i = 0; i < len; i++) {
      let node = queue.shift();
      // 与层次遍历区别在于,res要求的返回形式不同,所以这里不需要 临时数组 储存每一层的遍历结果。
      res.push(node.val);
      node.left && queue.push(node.left);
      node.right && queue.push(node.right);
    }
  }
  return res;
};

参考题解

代码随想录:二叉树层序遍历

代码随想录:个人blog