BFS总结

Posted pdev

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BFS总结相关的知识,希望对你有一定的参考价值。

能够用 BFS 解决的问题,一定不要用 DFS 去做!
因为用 Recursion 实现的 DFS 可能造成 StackOverflow!
(NonRecursion 的 DFS 一来你不会写,二来面试官也看不懂)

 

1. Queue

Python中,使用collections.deque,双端队列

 1 class MyQueue:
 2     # 队列初始化
 3     def __init__(self):
 4         self.elements = []  # 用list存储队列元素
 5         self.pointer = 0    # 队头位置
 6 
 7     # 获取队列中元素个数
 8     def size(self):
 9         return len(self.elements)-pointer
10     
11     # 判断队列是否为空
12     def empty(self):
13         return self.size() == 0
14 
15     # 在队尾添加一个元素
16     def add(self, e):
17         self.elements.append(e)
18 
19     # 弹出队首元素,如果为空则返回None
20     def poll(self):
21         if self.empty():
22             return None
23         pointer += 1
24         return self.elements[pointer-1]

 

2. 图的BFS

BFS中可能用到的HashSet(C++: unordered_map, Python: dict)

常用邻接表存储。邻接矩阵太大了...

邻接表定义:

1. 自定义的方法,更加工程化。所以在面试中如果时间不紧张题目不难的情况下,推荐使用自定义邻接表的方式。

1 def DirectedGraphNode:
2     def __init__(self, label):
3         self.label = label
4         self.neighbors = []  # a list of DirectedGraphNode‘s
5         ...
6 其中 neighbors 表示和该点连通的点有哪些

2. 使用 Map 和 Set

 这种方式虽然没有上面的方式更加直观和容易理解,但是在面试中比较节约代码量。

 1 # 假设nodes为节点标签的列表:
 2 
 3 # 使用了Python中的dictionary comprehension语法
 4 adjacency_list = {x:set() for x in nodes}
 5 
 6 # 另一种写法
 7 adjacency_list = {}
 8 for x in nodes:
 9     adjacency_list[x] = set()
10 其中 T 代表节点类型。通常可能是整数(Integer)。

 

3. 拓扑排序

http://www.lintcode.com/problem/topological-sorting/

http://www.jiuzhang.com/solutions/topological-sorting/

 

4. BFS模板

4.1 无需分层遍历

 1 from collections import deque
 2 
 3 queue = deque()
 4 seen = set()  #等价于Java版本中的set
 5 
 6 seen.add(start)
 7 queue.append(start)
 8 while len(queue):
 9     head = queue.popleft()
10     for neighbor in head.neighbors:
11         if neighbor not in seen:
12             seen.add(neighbor)
13             queue.append(neighbor)
14 
15 
16 neighbor 表示从某个点 head 出发,可以走到的下一层的节点。
17 set存储已经访问过的节点(已经丢到 queue 里去过的节点)
18 queue 存储等待被拓展到下一层的节点
19 set与 queue 是一对好基友,无时无刻都一起出现,往 queue 里新增一个节点,就要同时丢到 set 里。

4.2 需要分层遍历

 1 from collections import deque
 2 
 3 queue = deque()
 4 seen = set()
 5 
 6 seen.add(start)
 7 queue.append(start)
 8 while len(queue):
 9     size = len(queue)
10     for _ in range(size):
11         head = queue.popleft()
12         for neighbor in head.neighbors:
13             if neighbor not in seen:
14                 seen.add(neighbor)
15                 queue.append(neighbor)
16 
17 
18 size = queue.size() 是一个必须的步骤。如果在 for 循环中使用 for (int i = 0; i < queue.size(); i++) 会出错,因为 queue.size() 是一个动态变化的值。所以必须先把当前层一共有多少个节点存在局部变量 size 中,才不会把下一层的节点也在当前层进行扩展。

 

1. 二叉树上的BFS

http://www.lintcode.com/problem/binary-tree-level-order-traversal/   Video 11:00

 

2. Binary Tree的序列化/反序列化

http://www.lintcode.com/en/help/binary-tree-representation/
http://www.lintcode.com/en/problem/binary-tree-serialization/

 

Binary Tree Level Order Traversal II
http://www.lintcode.com/en/problem/binary-tree-level-order-traversal-ii/
http://www.jiuzhang.com/solutions/binary-tree-level-order-traversal-ii/

 

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/

 

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/

 

3. 图的BFS

http://www.lintcode.com/problem/clone-graph/   Video 43:00

http://www.lintcode.com/problem/word-ladder/   Video  55:00

4. 矩阵的bfs

图 Graph
N个点,M条边
M最大是 O(N^2) 的级别
图上BFS时间复杂度 = O(N + M)
? 说是O(M)问题也不大,因为M一般都比N大
所以最坏情况可能是 O(N^2)

矩阵 Matrix
R行C列
R*C个点,R*C*2 条边(每个点上下左右4条边,每条边被2个点共享)。
矩阵中BFS时间复杂度 = O(R * C)

 

http://www.lintcode.com/problem/number-of-islands/   Video 1:30:00

http://www.lintcode.com/problem/knight-shortest-path/   Video 1:35:00

 

5. 拓扑排序

入度(In-degree):
有向图(Directed Graph)中指向当前节点的点的个数(或指向当前节点的边的条数)
算法描述:
1. 统计每个点的入度
2. 将每个入度为 0 的点放入队列(Queue)中作为起始节点
3. 不断从队列中拿出一个点,去掉这个点的所有连边(指向其他点的边),其他点的相应的入度 - 1
4. 一旦发现新的入度为 0 的点,丢回队列中
拓扑排序并不是传统的排序算法
一个图可能存在多个拓扑序(Topological Order),也可能不存在任何拓扑序

拓扑排序的四种不同问法:
    求任意1个拓扑序(Topological Order)
    问是否存在拓扑序(是否可以被拓扑排序)
    求所有的拓扑序                  DFS
    求是否存在且仅存在一个拓扑序        Queue中最多同时只有1个节点

 

http://www.lintcode.com/problem/topological-sorting/

 

http://www.lintcode.com/problem/course-schedule/
http://www.lintcode.com/problem/course-schedule-ii/

 

http://www.lintcode.com/problem/alien-dictionary/
相似问题http://www.lintcode.com/problem/sequence-reconstruction/

 

? 图上的BFS
? 判断一个图是否是一棵树
? http://www.lintcode.com/problem/graph-valid-tree/
? 搜索图中最近值为target的点
? http://www.lintcode.com/problem/search-graph-nodes/
? 无向图联通块
? http://www.lintcode.com/problem/connected-component-in-undirected-graph/
? 序列重构(判断是否只有一个拓扑排序)
? http://www.lintcode.com/problem/sequence-reconstruction/
? 矩阵上的BFS
? 僵尸多少天吃掉所有人
? http://www.lintcode.com/problem/zombie-in-matrix/
? 建邮局问题 Build Post Office II
? http://www.lintcode.com/problem/build-post-office-ii/

 

双向BFS

https://www.jiuzhang.com/tutorial/algorithm/371

 
























以上是关于BFS总结的主要内容,如果未能解决你的问题,请参考以下文章

BFS实现8数码问题,思考与总结

python常用代码片段总结

BootStrap有用代码片段(持续总结)

BootStrap实用代码片段(持续总结)

回归 | js实用代码片段的封装与总结(持续更新中...)

BFS与DFS模板总结