--------转自 每日一道算法题 公众号
树的遍历是一个基础问题,也有很多的实际应用,可以用来找到匹配的字符串、文本分词和文件路径等问题。
数的遍历有两个基本的方法:深度优先遍历 和 广度优先遍历 。
深度优先遍历又根据处理节点的顺序不同,可以分为:中序遍历、前序遍历和后序遍历。这些知识点也是深度优先遍历经常考察的。
广度优先遍历的考察在于层次遍历,比如需要我们按照层次输出一棵树的所有节点的组合(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 != 03 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 != 03 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.val11 else //说明之前level没有访问过12 ans.[level] = [node.val]13 visit (node.left, level +1,ans)14 visit (node.right, level +1,ans)
逻辑处理部分的代码还有一个需要注意的地方,比如LeetCode 107 需要求reverse后的结果,所以处理这一部分逻辑代码时,需要找到当前level对应的数组的position。
总结
树的难点在于树的构造,需要将一般性问题抽象成一棵树,需要定义好节点和路径。当我们构造好一棵树后,一般只需要遍历这棵树就能得到结果。所以树的遍历是一个基础问题。
其中分层遍历的技巧比dfs/bfs更难点,需要有更强的逻辑思维能力。