1.基本概念
图的遍历:从图中某个顶点出发访遍图中其余顶点,并且使图中的每个顶点仅被访问一次 过程。
遍历图的过程实质上是通过边或弧找对每个顶点查找其邻接点的过程,其耗费的 时间取决于所采用的存储结构。当用邻接矩阵作图的存储结构时,查找每个顶点 的邻接点所需要时间为O(n2),n为图中顶点数;而当以邻接表作图的存储结构 时,找邻接点所需时间为O(e),e为无向图中边的数或有向图中弧的数。
![](/images/cnblogs_com/xiaotaoliang/图的遍历/7_2_3.gif)
广度优先搜索遍历图的时间复杂度和深度优先搜索遍历相同,两者不同之处在于 对顶点房屋的顺序不同。
2.深度优先搜索
从图中某个顶点V0 出发,访问此顶点,然后依次从V0的各个未被访问的邻接点出发深度优 先搜索遍历图,直至图中所有和V0有路径相通的顶点都被访问到,若此时图中尚有顶点未被访 问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问 到为止。
当以邻接表作存储结构时,深度优先搜索遍历图的时间复杂度为O(n+e)。
//--- 下列算法使用的全局变量 ---
Boolean visited[MAX]; // 访问标志数组
Status (* VisitFunc)(int v); // 函数变量
void DFSTraverse(Graph G, Status (*Visit)(int v)) { // 对图G作深度优先遍历。
VisitFunc = Visit;
for (v=0; v<G.vexnum; ++v)
visited[v] = FALSE; // 访问标志数组初始化
for (v=0; v<G.vexnum; ++v)
if (!visited[v]) DFS(G, v); // 对尚未访问的顶点调用DFS
}
void DFS(Graph G, int v) { // 从第v个顶点出发递归地深度优先遍历图G。
visited[v] = TRUE; VisitFunc(v); // 访问第v个顶点
for ( w=FirstAdjVex(G, v); w!=0; w=NextAdjVex(G, v, w) )
if (!visited[w]) DFS(G, w); // 对v的尚未访问的邻接顶点w递归调用DFS
}
3.广度优先搜索
从图中的某个顶点V0出发,并在访问此顶点之后依次访问V0的所有未被访问过的邻接点,之 后按这些顶点被访问的先后次序依次访问它们的邻接点,直至图中所有和V0有路径相通的顶点都 被访问到。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上 述过程,直至图中所有顶点都被访问到为止。
void BFSTraverse(Graph G, Status (*Visit)(int v)) {
// 按广度优先非递归遍历图G。使用辅助队列Q和访问标志数组visited。
for (v=0; v<G.vexnum; ++v) visited[v] = FALSE;
InitQueue(Q); // 置空的辅助队列Q
for ( v=0; v<G.vexnum; ++v )
if ( !visited[v]) { // v尚未访问
EnQueue(Q, v); // v入队列
while (!QueueEmpty(Q)) {
DeQueue(Q, u); // 队头元素出队并置为u
visited[u] = TRUE; Visit(u); // 访问u
for ( w=FirstAdjVex(G, u); w!=0; w=NextAdjVex(G, u, w) )
if ( ! visited[w]) EnQueue(Q, w); // u的尚未访问的邻接顶点w入队列Q
};
}
} // BFSTraverse
遍历应用举例:
1. 求一条从顶点i到顶点s 的简单路径
void DFSearch( int v, int s, char *PATH){
// 从第v个顶点出发递归地深度优先遍历图G,求得一条从v到s的简单路径,并记录在PATH中
visited[v] = TRUE; // 访问第v个顶点
Append(PATH, getVertex(v)); // 将第v个顶点添加到路径PATH上
for (w=FirstAdjVex(v); w!=0&&!found;
w=NextAdjVex(v) )
if (w=s) { found = TRUE; Append(PATH, w); }
else if (!visited[w]) DFSearch(w, LP);// 对v的尚未访问的邻接顶点w递归调用DFS
if (!found) Delete (PATH); // 删除路径上最后一个顶点
}
2. 求两个顶点之间的一条路径长度最短的路径
基于广度优先搜索遍历,并修改链队列的结点结构及其入队列和出队列的算法。
1. 将链队列的结点改为“双链”结点,即结点中包含next 和priou两个指针;
2. 修改入队列的操作,插入新的队尾结点时,令其priou域的指针指向刚刚出队列的结点, 即当前的队头指针所指结点;
3. 修改出队列的操作,出队列时,仅移动队头指针,而不将队头结点从链表中删除。
例如: 求下图中顶点3至顶点5的一条最短路径。
链队列的状态如下所示:
![](/images/cnblogs_com/xiaotaoliang/图的遍历/7_2_4.gif)
typedef DuLinkList QueuePtr;
void InitQueue(LinkQueue& Q) {// 初始化链队列,创建头结点
Q.front = Q.rear = (QueuePtr) malloc(sizeof(QNode));
Q.front->next = Q.rear->next = NULL
}
void EnQueue( LinkQueue& Q, QelemType e ) {// 入队列操作,新的队尾结点的priou指针指 向队头
p = (QueuePtr) malloc (sizeof(QNode));
p->data = e; p->next = NULL;
p->priou = Q.front
Q.rear->next = p; Q.rear = p;
}
void DeQueue( LinkQueue& Q, QelemType& e ) {// 出队列操作,向后移动队头指针
Q.front = Q.front->next; e = Q.front->data
}