JS算法专题 - 树的层序遍历

先看看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的题目

层序遍历二叉树.png

解题:

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上的总结:
二叉树层序遍历登场:我要打十个!

你可能感兴趣的:(JS算法专题 - 树的层序遍历)