广度优先遍历算法考察频度很高!
广度优先遍历(bfs)需要借助队列来实现,因为队列先进先出、符合一层一层遍历的逻辑。同理栈先进后出的特性,适合用来模拟深度优先遍历也就是递归的逻辑。
「二叉树层序遍历思路」
-
如果是空树,直接
return [] -
首先根元素入队,
-
当队列不为空时(
while循环)- 记录当前队列的长度
len(当前层级的节点数) - 一次性遍历完当前层的
len个节点,并拓展操作 - 最后将这层的结果放到结果数组。
- 记录当前队列的长度
注意:
- 队列先进先出要用
shift和push表示。 arr.push(cur)中的参数cur如果是数组,那么就会把一整个数组'[]'推到arr中。
const arr = [1, 2];
const cur = [3, 4];
arr.push(cur);
console.log(arr); // [ 1, 2, [ 3, 4 ] ]
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
只需要在102.二叉树的层序遍历基础上,最后把结果数组res反转一下就可以了。
注意,reverse()会改变原数组。
return res.reverse();
注意,对于多维数组,reverse()只会反转第一层,不会深入反转。
const arr = [ 1, 2, [ 3, 4 ] ];
console.log(arr.reverse()); // [ [ 3, 4 ], 2, 1 ]
拓展二:429. N叉树的层序遍历
N叉树和二叉树的区别在于N叉树的子节点不止左右两个,所以不能再使用node.left和node.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. 二叉树的锯齿形层序遍历
锯齿形层次遍历就是,先从左往后,下一层再从右往左,如此反复进行。
通过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. 二叉树的右视图
层序遍历的时候,判断是否遍历到当前层级的最后一个元素,是就放进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.从上到下打印二叉树
这题的思路和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;
};
参考题解