树的层次遍历的几种方法

--------转自 每日一道算法题  公众号

树的遍历是一个基础问题,也有很多的实际应用,可以用来找到匹配的字符串、文本分词和文件路径等问题。

数的遍历有两个基本的方法:深度优先遍历 和 广度优先遍历 。

深度优先遍历又根据处理节点的顺序不同,可以分为:中序遍历、前序遍历和后序遍历。这些知识点也是深度优先遍历经常考察的。

广度优先遍历的考察在于层次遍历,比如需要我们按照层次输出一棵树的所有节点的组合(LeetCode 107),比如求一棵树的最左节点(LeetCode 513).这些

问题本质上都是考察的广度优先遍历。

如下代码是经典的广度优先遍历实现的方式,使用了队列的FIFO的方式,将所有暂未访问的节点存入一个队列,依次遍历。

queue = [node]  // 新建一个队列,并将根节点放入队列
while queue.length != 0
    item = queue.shift   // 弹出队列的头部元素
    do_someting(item)  // 操作该节点:比如存入一个数组或者打印
    queue.push(item.left)  if item.left    // 将左子节点存入队列
    queue.push(item.right) if item.right  // 将右子节点存入队列

但是只掌握了上面的遍历方法是不够的,层次遍历的难点在于 层次的比较 。也就是说,我们需要对不同层次的节点做 隔离 

--------------------------------------------正文---------------------------------------------------------------------------------------------------------------------------------------------

遍历技巧一: 数组长度做隔离

思路:

获取当前的队列的长度length,一次只遍历length个节点,后续加入的元素在下一次循环遍历。

伪代码如下:

1 queue =  [node]  // 新建一个队列,并将根节点放入队列
2 while queue.lengh != 0
3     length = queue.length //  获取当前队列的长度
4     while length > 0        // 只弹出length 个节点
5         item = queue.shift // 弹出队列的头部元素
6         do_something(item)    //  操作该节点:比如存入一个数组或者打印
7         queue.push(item.left)  if item.left  // 将左子节点存入队列
8         queue.push(item.right) if item.right  // 将右子节点存入队列
9         length--

遍历技巧二:使用分隔符

思路

在不同的节点中间加入一个分隔符,遍历发哦分割几点的时候,停止当前遍历。

伪代码如下:

1 queue = [node]     // 新建一个队列,并将根节点放入到队列
2 while queue.lengh != 0
3     queue.push "$"    // 将分割符放入队列
4     while(true)           // 做一个无限循环
5            item = queue.shift   // 弹出队列的头部元素
6            break if item == '$'   // 如果当前的节点等于分隔符,说明该层已经遍历到了最右边
7            do_something(item)  //操作该节点
8            queue.push(item.left)  if item.left // 将左子节点存入队列
9            queue.push(item.right)  if item.right // 将右子节点存入队列

遍历技巧三:使用深度优先搜索

思路:

用一个level字段来保存深度,在深度优先遍历的时候,判断一下当前结点的深度即可

伪代码如下:

 1 ans = []  // 用一个数组来保存值
 2 level = 0 // 根节点的level 是0
 3 visit(node,ans,level)
 4 
 5 
 6 def visit(node,ans,level):
 7       return if node is null  // 如果节点为空,则返回
 8     //逻辑处理部分
 9     if ans.lengh > level   //说明之前访问过该层的节点
10            ans.[level].push node.val
11      else               //说明之前level没有访问过
12            ans.[level] = [node.val]
13 visit (node.left, level +,ans)
14 visit (node.right, level +,ans)

逻辑处理部分的代码还有一个需要注意的地方,比如LeetCode 107 需要求reverse后的结果,所以处理这一部分逻辑代码时,需要找到当前level对应的数组的position。

总结

树的难点在于树的构造,需要将一般性问题抽象成一棵树,需要定义好节点和路径。当我们构造好一棵树后,一般只需要遍历这棵树就能得到结果。所以树的遍历是一个基础问题。

其中分层遍历的技巧比dfs/bfs更难点,需要有更强的逻辑思维能力。

原文地址:https://www.cnblogs.com/simplepaul/p/6721687.html