先看看leetcode上的几道题目,关键字层序遍历
,其实就是把一棵树一层一层地遍历,取出每一个节点。当然从根节点到叶子节点,从叶子节点到根节点,每层从左到右,从右到左……都可以衍生成不同的题目。
102. 二叉树的层序遍历
107. 二叉树的层序遍历 II
429. N 叉树的层序遍历
这一类的题目,被归纳在leetcode的广度优先搜索
标签下
广度优先搜索算法(Breadth-First Search,缩写为 BFS)
,又称为宽度优先搜索,是一种图形搜索算法。简单的说,BFS 是从根结点开始,沿着树的宽度遍历树的结点。如果所有结点均被访问,则算法中止。
这一类的题目,总结起来可以套用模板来解决,我们需要借用一下数据结构的知识:
队列
:先进先出,后进后出
在JavaScript中,我们会使用数组Array
来模拟队列:
-
Array.unshift()
从数头部插入一个数 -
Array.pop()
从数组尾部弹出一个数 -
Array.push()
从数组尾部插入一个数 -
Array.shift()
从数组头部弹出一个数
先初始化一个二叉树
function TreeNode(val, left, right){
this.val = (val===undefined ? 0 : val)
this.left = (left===undefined ? null : left)
this.right = (right===undefined ? null : right)
}
// 二叉树:[3,9,20,null,null,15,7]
let tree = new TreeNode(3,new TreeNode(9),new TreeNode(20, new TreeNode(15), new TreeNode(7)));
先看看102的题目
解题:
var levelOrder = function(root) {
let res = [];
if(!res) return res;
// 创建一个队列,把根元素放进去
let queue = [root];
// 只要队列中有元素,继续循环
while(queue.length){
// 用来存放每一层的节点的值
let level = [];
// 每一层的节点数,当前队列中的节点都是上一层的子节点
let len = queue.length;
// 遍历每一层的节点
for(let i=0; i< len; i++){
// 弹出队列最后一个节点
let node = queue.pop();
level.push(node.val);
// 二叉树:如果有左子节点或右子节点,把子节点放入队列最前面,等待下一次遍历
if(node.left){
queue.unshift(node.left);
}
if(node.right){
queue.unshift(node.right);
}
}
// 同一层的节点遍历结束
res.push(level);
}
return res;
};
let result = levelOrder(tree); // [ [ 3 ], [ 9, 20 ], [ 15, 7 ] ]
从以上例子,只要步骤是:
- 把每一层节点放进队列中
- 遍历队列中的节点
有一个细节要注意:
let node = queue.pop();
queue.unshift(node.left);
这里用的是pop()
和unshift()
,是因为队列先进先出的特点,pop()从最后面弹出一个元素,unshift()把新元素放到最前面。push()
和shift()
搭配也有相同效果。
107的题目稍微改变了一下,要求输出是[[15,7], [9,20], [3]],可以看出,就是把102的从上到下输出每一层节点,改成从下到上输出,首先想到的就是把102的数组反转就可以了。
但是,我们可能直接在保存结果数组的时候处理一下:
102中,我们使用push()
把每一层的节点放入结果数组
res.push(level); // [ [ 3 ], [ 9, 20 ], [ 15, 7 ] ]
107中,结果数组和102的反转了,所以,我们可以把push()
改成unshift()
即可,最后得到的结果就是,树的要节点那层在结果数组的最后,叶子节点在最前面。
res.unshift(level); // [ [ 15, 7 ], [ 9, 20 ], [ 3 ] ]
429题中把二叉树改成了N叉树,树的结构发生了变化
function Node(val,children) {
this.val = val;
this.children = children;
};
但是可以看到,和二叉树的数据结构相比,就是把left和right合成children,那么在我们的模板中,也只需要修改一下
if(node.left){
queue.unshift(node.left);
}
if(node.right){
queue.unshift(node.right);
}
改成
if(node.children){
node.children.forEach(child => {
queue.unshift(child);
});
}
这样就可以得到结果了。
想一下,如果再改一下:如何从右到左遍历每一层节点?层数是单数时从左到右,层数是双数时从右到左?
再扩展一下,这个算法能做什么呢?
当我们玩游戏,特别是走迷宫之类的游戏,是不是会从一个点出发,然后判断下一步能不能走,直到找出终点?BFS也是寻路方案的基础。
推荐一下leetcode上的总结:
二叉树层序遍历登场:我要打十个!