文章目录
重点
- 深度优先遍历
- 广度优先遍历
- 图遍历算法的应用:拓扑排序、连通性问题、关键路径。
1. 图的遍历
图的遍历是指从图的某一个顶点出发,按照某种搜索方法沿着图中的边对图中的所有结点访问一次且仅一次。
1.2 广度优先搜索
(1)核心问题
- 搜索相邻结点
- 判断已经访问过的结点
- 辅助队列
- 如何处理非连通图
(2)思想
广度优先搜索类似于二叉树的层次遍历算法。
- 首先访问起始顶点,接着访问其未被访问过的邻近顶点。
- 再从被访问的顶点出发,访问它未被访问过的邻近结点。直到所有顶点被访问为止。
- 若仍有顶点未被访问,则另选未被访问的结点作为起始,重复上述过程。
理解:
-
每经历一次广度优先搜索,图中的一个连通分量就会完成访问。因此调用BFS的次数等于图中连通分量的个数。
-
图的广度优先搜索与树的层次遍历思想相似。只是多使用了一个visited[]数组记录节点是否被访问过。
// 广度优先遍历
bool visited[MAX_VERTEX];
void BFSTraverse(Gragh G){
// 初始化vistied向量
for(int i=0; i<MAX_VERTEX; i++)
visited[i] = false;
// 遍历未访问结点(所有连通分支)
for(int i=0; i<MAX_VERTEX; i++)
if(! visited[i])
BFS(G, i);
}
// 单个连通分支
void BFS(Gragh G, int v){
queue<int> q;
q.push(v);
while(!q.empty()){
int temp = q.top();
q.pop();
for(int w=FirstNeighbor(G,v); w>=0; w=NextNeighbor(G,v,w)){
// 未被访问的邻近结点
if(!visited[w]){
visited[w] = true;
q.push(w);
}
}
}
}
(3)性能分析
采用邻接表或邻接矩阵存储的广度优先算法的复杂度是不一样的。
- 空间复杂度: BST算法需要一个辅助队列,最坏情况下空间复杂度为 O ( ∣ V ∣ ) O(|V|) O(∣V∣)。
- 时间复杂度: 首先每个顶点都需要搜索一次,时间复杂度为
O
(
∣
V
∣
)
O(|V|)
O(∣V∣)。
①若采用邻接表存储,需要访问所有的边。故时间复杂度为 O ( ∣ E ∣ + ∣ V ∣ ) O(|E|+|V|) O(∣E∣+∣V∣)。
②若采用邻接矩阵存储,需要访问每个节点的邻接顶点。故时间复杂度为 O ( ∣ V ∣ 2 ) O(|V|^2) O(∣V∣2)。
1.3 深度优先搜索
(1)核心问题
- 搜索相邻节点。
- 判断已经访问过的节点。
- 如何处理非连通子图。
(2)思想
DFS类似先序遍历,按照根节点–>左孩子–>右孩子的顺序访问图。但是多增加了visited[]数组记录节点是否已经访问。因为一次DFS搜索后能够完成遍历一个连通分支,因此为了处理非连通子图问题,需要首先遍历访问所有节点,用于处理一次DFS后未访问的分支。
bool visited[MAX_VERTEX_NUM];
void DFSTraverse(Gragh G) {
// 初始化visited
for(int i=0; i<MAX_VERTEX_NUM; i++)
visited[i] = false;
// 遍历所有节点
for(int v=0; v<MAX_VERTEX_NUM; v++) {
if(!visited[v])
DFS(G, v);
}
}
void DFS(Gragh G, int v) { // 从顶点v出发,深度遍历图G
visit(v);
visited[v] = true;
for(int i=FirstNeighbor(G, v); i>0; i =NextNeighbor(G,v,i)) {
if(!visited[i]) // 尚未访问的节点
DFS(G, i);
}
}
(3)性能分析
- 空间复杂度: 借助一个递归工作栈,空间复杂度为 O ( ∣ V ∣ ) O(|V|) O(∣V∣)。
- 时间复杂度: 首先每个顶点都需要搜索一次的时间复杂度为
O
(
∣
V
∣
)
O(|V|)
O(∣V∣)。
①若采用邻接表存储,需要访问所有的边。故时间复杂度为 O ( ∣ E ∣ + ∣ V ∣ ) O(|E|+|V|) O(∣E∣+∣V∣)。
②若采用邻接矩阵存储,需要访问每个节点的邻接顶点。故时间复杂度为 O ( ∣ V ∣ 2 ) O(|V|^2) O(∣V∣2)。