模板,记住这5个:
(1)针对树的BFS
1.1 无需分层遍历
from collections import deque
def levelOrderTree(root):
if not root:
return
q = deque([root])
while q:
head = q.popleft()
do something with this head node...
if head.left:
q.append(head.left)
if head.right:
q.append(head.right)
return xxx
1.2 需要分层遍历
def levelOrderTree(root):
if not root:
return
q = [root]
while q:
new_q = []
for node in q: # 和上面代码相比 差异就在这里 和 deque
do something with this layer nodes...
if node.left:
new_q.append(node.left)
if node.right:
new_q.append(node.right)
q = new_q
return xxx
(2)针对图的BFS
2.1 无需分层遍历
from collections import deque
def bfs_graph(root):
if not root:
return
queue = deque([root])
seen = set([root])
while queue:
head = queue.popleft()
do something with this head...
for neighbor in head.neighbors:
if neighbor not in seen: # 和tree的区别无非就是多了一个是否访问过的判断
seen.add(neighbor)
queue.append(neighbor)
return xxx
上述代码中:
neighbor 表示从某个点 head 出发,可以走到的下一层的节点。
set/seen 存储已经访问过的节点(已经丢到 queue 里去过的节点)
queue 存储等待被拓展到下一层的节点
set/seen 与 queue 是一对好基友,无时无刻都一起出现,往 queue 里新增一个节点,就要同时丢到 set 里。
需要分层遍历的宽度搜先搜索
2.2 需分层遍历
def bfs_graph(root):
if not root:
return []
q = [root]
seen = set([root])
while q:
new_q = []
for node in q:
do something with this layer nodes...
for neighbor in node.neighbors:
if neighbor not in seen: # 和tree的区别无非就是多了一个是否访问过的判断
seen.add(neighbor)
new_q.append(neighbor)
q = new_q
return xxx
(3)拓扑排序
记住下面的代码
class Solution:
"""
@param graph: A list of Directed graph node
@return: Any topological order for the given graph.
"""
def topSort(self, graph):
node_to_indegree = self.get_indegree(graph)
# bfs
order = []
start_nodes = [n for n in graph if node_to_indegree[n] == 0]
queue = collections.deque(start_nodes)
while queue:
node = queue.popleft()
order.append(node)
for neighbor in node.neighbors:
node_to_indegree[neighbor] -= 1
if node_to_indegree[neighbor] == 0:
queue.append(neighbor)
return order
def get_indegree(self, graph):
node_to_indegree = {x: 0 for x in graph}
for node in graph:
for neighbor in node.neighbors:
node_to_indegree[neighbor] += 1
return node_to_indegree
算法流程
拓扑排序的算法是典型的宽度优先搜索算法,其大致流程如下:
统计所有点的入度,并初始化拓扑序列为空。
将所有入度为 0 的点,也就是那些没有任何依赖的点,放到宽度优先搜索的队列中
将队列中的点一个一个的释放出来,放到拓扑序列中,每次释放出某个点 A 的时候,就访问 A 的相邻点(所有A指向的点),并把这些点的入度减去 1。
如果发现某个点的入度被减去 1 之后变成了 0,则放入队列中。
直到队列为空时,算法结束
一些实际案例:
https://www.cnblogs.com/bonelee/p/11724346.html
BFS宽度优先搜索
模板:
https://www.cnblogs.com/bonelee/p/11724346.html
BFS模板:
def levelOrder(self, root):
# write your code here
if not root:
return []
q = [root]
result = []
while q:
children = []
q2 = []
for node in q:
children.append(node.val)
if node.left:
q2.append(node.left)
if node.right:
q2.append(node.right)
result.append(children)
q = q2
return result
BFS 递归实现
q= [root]
result = []
def levelOrder(q):
if not q:
return
q2 = []
for node in q:
xxxx
if node.left: q2.append(node.left)
if node.right: q2.append(node.right)
levelOrder(q2)
1. BFS VS DFS
https://www.geeksforgeeks.org/difference-between-bfs-and-dfs/
2. 什么时候使用BFS
图的遍历 Traversal in Graph
• 层级遍历 Level Order Traversal
• 由点及面 Connected Component
• 拓扑排序 Topological Sorting
最短路径 Shortest Path in Simple Graph
• 仅限简单图求最短路径
• 即,图中每条边长度都是1,或者边长都相等
非递归的方式找所有方案 Iteration solution for all possible results
• 这一点我们将在后面 DFS 的课上提到
3. 题型与算法的对应关系
最短路径
简单图 → BFS
复杂图 → Dijkstra, SPFA, Floyd(一般面试不考这个)
最长路径
图可以分层 → Dynamic Programming
分层:比如走到第i一定会经过第 i-1 层(棋盘格子图是典型的例子)
不可以分层 → DFS
4.主要内容
• 二叉树上的 BFS
• 图上的 BFS
• 矩阵上的 BFS
• 拓扑排序 Topological Sorting
5. 二叉树上的 BFS
5.1 二叉树层级遍历
5.1.1 非递归实现方法讲解
https://blog.csdn.net/sinat_20177327/article/details/78285495
5.1.2 题目和非递归python实现
https://blog.csdn.net/yurenguowang/article/details/76906620
5.1.3 DFS递归方法和java实现
https://blog.csdn.net/crazy1235/article/details/51507485
5.2 二叉树的序列化
5.2.1 什么是序列化(P11)
将“内存”中结构化的数据变成“字符串”的过程
序列化:object to string
反序列化:string to object
5.2.2 什么时候需要序列化(P12)
1. 将内存中的数据持久化存储时
内存中重要的数据不能只是呆在内存里,这样断电就没有了,所需需要用一种方式写入硬盘,在需要的
时候,能否再从硬盘中读出来在内存中重新创建
2. 网络传输时
机器与机器之间交换数据的时候,不可能互相去读对方的内存。只能讲数据变成字符流数据(字符串)后
通过网络传输过去。接受的一方再将字符串解析后到内存中。
5.2.3 序列化算法(P13)
一些序列化的例子:
• 比如一个数组,里面都是整数,我们可以简单的序列化为”[1,2,3]”
• 一个整数链表,我们可以序列化为,”1->2->3”
• 一个哈希表(HashMap),我们可以序列化为,”{”key”: ”value”}”
序列化算法设计时需要考虑的因素:
• 压缩率。对于网络传输和磁盘存储而言,当然希望更节省。
• 如 Thrift, ProtoBuf 都是为了更快的传输数据和节省存储空间而设计的。
• 可读性。我们希望开发人员,能够通过序列化后的数据直接看懂原始数据是什么。
• 如 Json,LintCode 的输入数据
5.2.4 使用BFS序列化
题目
https://www.lintcode.com/problem/serialize-and-deserialize-binary-tree/description
python BFS解答
https://www.jiuzhang.com/solutions/binary-tree-serialization/#tag-highlight-lang-python
5.3 二叉树上的BFS相关题目
5.3.1 Binary Tree Level Order Traversal II
题目:
http://www.lintcode.com/en/problem/binary-tree-level-order-traversal-ii/
解答:
https://blog.csdn.net/coder_orz/article/details/51583729
http://www.jiuzhang.com/solutions/binary-tree-level-order-traversal-ii/
5.3.2 Binary Tree Zigzag Order Traversal
http://www.lintcode.com/en/problem/binary-tree-zigzag-level-order-traversal/
http://www.jiuzhang.com/solutions/binary-tree-zigzag-level-order-traversal/
5.3.3 Convert Binary Tree to Linked Lists by Depth
http://www.lintcode.com/en/problem/convert-binary-tree-to-linked-lists-by-depth/
http://www.jiuzhang.com/solutions/convert-binary-tree-to-linked-lists-by-depth/
leetcode
lintcode(大部分提供解答)