广度优先搜索&深度优先搜索(Breadth First Search & Depth First Search)
BFS优缺点:
- 同一层的所有节点都会加入队列,所以耗用大量空间;
- 仅能非递归实现;
- 相比DFS较快,空间换时间;
- 适合广度大的图;
- 空间复杂度:邻接矩阵O(N^2);邻接表O(N+E);
- 时间复杂度:O(V+E);
DFS优缺点:
- 无论是系统栈还是用户栈保存的节点数都只是树的深度,所以空间耗用小;
- 有递归和非递归实现;
- 由于有大量栈操作(特别是递归实现时候的系统调用),执行速度较BFS慢;
- 适合深度大的图;
- 空间复杂度:邻接矩阵O(N^2);邻接表O(N+E);
- 时间复杂度:O(V+E);
1 //BFS只能非递归实现,将queue替换成stack之后就是DFS
2 procedure BFS(G,v):
3 create a queue Q
4 enqueue v onto Q
5 mark v
6 while Q is not empty:
7 t ← Q.dequeue()
8 if t is what we are looking for:
9 return t
10 for all edges e in G.incidentEdges(t) do //遍历所有与t直接相连的边e
11 o ← G.opposite(t,e) //通过边e与t相连的顶点o
12 if o is not marked:
13 mark o
14 enqueue o onto Q
15
16 //DFS递归实现
17 procedure DFS(G,v):
18 label v as explored
19 for all edges e in G.incidentEdges(v) do
20 if edge e is unexplored then
21 w ← G.opposite(v,e)
22 if vertex w is unexplored then
23 label e as a discovery edge
24 recursively call DFS(G,w) //递归调用没有访问的顶点w
25 else
26 label e as a back edge
最小生成树算法(Minimum Spanning Tree Algorithm, eg: Kruskal, Prim)
Kruskal Algorithm
Kruskal属于贪心算法BFS策略,适用于稀疏图,使用并查集实现可具有较好性能,时间复杂度为O(ElogV),空间复杂度为O(V);
首先对于一个含有N个顶点的连通图,Kruskal首先构造一个含有N个独立顶点的图,也就是N棵只有一个顶点的树;然后从带有权值的边集合E中选择当前 权值最小的边e,如果e连接的顶点属于不同的树i和j,则使用e连接数i和j,并将e从边集合E中删除;然后从边集合E中选择下一个具有最小权值的边e, 直到左右的边都选择完毕;最终所有的N棵子树将合并成一棵树;
Prim Algorithm
- 时间复杂度:使用邻接矩阵实现为O(V^2);使用二叉堆和邻接表实现为O(ElogV);使用斐波那契堆实现为O(E+VlogV);Prim与 Kruskal的不同点在于,Prim从头到尾都只维护一棵树,通过从树的最远顶点相连的还未在树中的顶点中选择边权值最小的顶点进行扩展。
1 MST-PRIM(G,w,r)
2 Q←V[G] //将G的所有节点记录到Q
3 for 每个包含于Q的u
4 do key[u]←∞ //初始化每个节点的key,表示到根节点r的最短距离
5 key[r]←0 //处理根节点
6 p[r]←NIL
7 while Q≠空集
8 do u←EXTRACT-MIN(Q) //根据节点的key值选取一个最小的节点
9 for 每个包含于Adj[u]的节点v
10 do if v包含于Q and w(u,v)<key[v]
11 then p[v]←u
12 key[v]←w(u,v) //使用节点间的距离赋值节点v的最短距离