“这是我参与8月更文挑战的第26天,活动详情查看:8月更文挑战”
关注我,以下内容持续更新
图的遍历
图的遍历指的是从图中的任一顶点出发,对图中的所有顶点访问一次且只访问一次。图的遍历操作和树的遍历操作功能相似。图的遍历是图的一种基本操作,图的许多其它操作都是建立在遍历操作的基础之上。根据搜索策略的不同,图的遍历方法有深度优先搜索法和广度(宽度)优先搜索法两种算法。广度优先搜索是一层层的遍历,类似于树的层次遍历;深度优先搜索用白话说就是一条道走到黑,走到头再回溯继续往下遍历.例如下图的广度优先搜索顺序是:1、2、3、4、5、6、7、8;深度优先搜索顺序是:1、2、4、8、6、3、7、5。图的存储可以采用邻接矩阵或者邻接表,本文会针对这两种存储结构分别实现深度优先搜索和广度优先搜索。
一、广度优先搜索
图的广度优先搜索是树的按层次遍历的推广,在普利姆(Prim)最小生成树算法和迪杰斯特拉(Dijkstra)单源最短路径算法中,都采用了与广度优先搜索类似的思想。它的基本思想是:首先访问初始点v0,并将其标记为已访问过,接着访问v0的所有未被访问过的邻接点v1,v2,…, vi,并均标记已访问过,然后再按照v1,v2,…, vi的次序,访问每一个顶点的所有未被访问过的邻接点,并均标记为已访问过,依次类推,直到图中所有和初始点v0有路径相通的顶点都被访问过为止。
广度优先搜索可以利用队列先进先出的特点来实现,具体步骤如下:
1)从顶点v开始,标记它为已访问,将其压入队列中;
2)这时,顶点s的的相邻顶点还未被访问,所以v暂时是未探索状态;
3)访问它的所有相邻顶点,并将它们压入队列,之后v的状态变为已探索,将其从队列中删除;这些入队的顶点的父亲顶点即为v;
4)取队头顶点,以其为起点重复进行以上操作。当所有的顶点都被探索完毕,即队列为空时,探索停止。
通过上述分析得出,广度优先搜索的时间复杂度为O(V+E),其中V是顶点数,E是边数。
1. 邻接表存储图的广度优先搜索
广度优先搜索的非递归方式(while循环)的伪代码如下:
void BFS(int s){
queue<int> q;
q.push(s);
//只要队列仍然有元素,就会不断遍历,直到找到终点。
while(!q.empty()){
取队首;
访问队首;
弹出队首;
队首元素下一层级的元素全部入队;
}
}
代码实现:
/*找到顶点对应的下标*/
int LocateVex(ALGraph &G, char v)
{
int i;
for (i = 0; i < G.vexnum; i++)
if (v == G.vertices[i].data)
return i;
}
/*采用邻接表表示图的广度优先遍历*/
void BFS(ALGraph &G, char v0)
{
int u,w,v;
ArcNode *p;
printf("%c ", v0); //打印顶点v0
v = LocateVex(G, v0); //找到v0对应的下标
visited[v] = 1; //顶点v0已被访问
q.push(v0); //将顶点v0入队
while (!q.empty())
{
u = q.front(); //将顶点元素u出队,开始访问u的所有邻接点
v = LocateVex(G, u); //得到顶点u的对应下标
q.pop(); //将顶点u出队
//遍历顶点u的邻接点
for (p = G.vertices[v].firstarc; p; p = p->nextarc)
{
w = p->adjvex;
if (!visited[w])//顶点p未被访问
{
printf("%c ", G.vertices[w].data);//打印顶点p
visited[w] = 1; //顶点p已被访问
q.push(G.vertices[w].data);//将顶点p入队
}
}
}
}
2. 邻接矩阵存储图的广度优先搜索
/*采用邻接矩阵表示图的广度优先遍历*/
void BFS(AMGraph &G,char v0)
{
int u,i,v,w;
v = LocateVex(G,v0); //找到v0对应的下标
printf("%c ", v0); //打印v0
visited[v] = 1; //顶点v0已被访问
q.push(v0); //将v0入队
while (!q.empty())
{
u = q.front(); //将队头元素u出队,开始访问u的所有邻接点
v = LocateVex(G, u); //得到顶点u的对应下标
q.pop(); //将顶点u出队
for (i = 0; i < G.vexnum; i++)
{
w = G.vexs[i];
if (G.arcs[v][i] && !visited[i])//顶点u和w间有边,且顶点w未被访问
{
printf("%c ", w); //打印顶点w
q.push(w); //将顶点w入队
visited[i] = 1; //顶点w已被访问
}
}
}
}
二、深度优先搜索
深度优先搜索法是树的先序遍历的推广。它的基本思想是:从图G的某个顶点v0出发,访问v0,然后选择一个与v0相邻且未被访问的顶点vi进行访问,再从vi出发选择一个与vi相邻且未被访问的顶点vj进行访问,依次继续。如果当前被访问过的顶点的所有邻接顶点都已被访问,则退回到已被访问的顶点序列中最后一个拥有未被访问的相邻顶点的顶点w,从w出发按同样的方法向前遍历,直到图中所有顶点都被访问。深度优先搜索是一个递归的过程,代码可以用递归来实现
因为图的存储可以采用邻接矩阵或者邻接表,接下来针对这两种结构存储的图来实现深度优先搜索
1. 邻接表存储图
① 非递归算法(while循环)
伪代码如下:
1.访问顶点vi; visited[vi] = 1;
2. w=顶点vi的第一个邻接点;
3. while (w存在)
3.1 if (w未被访问)从顶点w出发递归执行该算法;
3.2 w=顶点v的下一个邻接点;
void DFS(ALGraph &G, int v)
{
int w;
printf("%c ", G.vertices[v].data);
visited[v] = 1;
ArcNode *p = new ArcNode;
p = G.vertices[v].firstarc;
while (p)
{
w = p->adjvex;
if (!visited[w]) DFS(G, w);
p = p->nextarc;
}
}
② 递归算法
Boolean visited[MAXSIZE]; /* 访问标志的数组 */
void DFS(GraphAdjList G, int i)
{
EdgeNode *p;
visited[i] = TRUE;
//2.打印顶点 A
printf("%c ",G->adjList[i].data);
p = G->adjList[i].firstedge;
//3.
while (p) {
if(!visited[p->adjvex])
DFS(G,p->adjvex);
p = p->next;
}
}
2. 邻接矩阵存储图
伪代码如下:
1.选择从某一个顶点开始访问
2.进入递归方法
2.1 打印顶点信息,visited[i] = true;
2.2 for循环遍历中做如下判断:
if(matrix[i][j]==1 && !visted[j]){
进入下次递归;
}
void DFSM(MGraph *G,int i)
{
int j;
printf("%c/n",G->vexs[i]); //访问顶点vi
visited[i]=TRUE;
for(j=0;j<G->n;j++) //依次搜索vi邻接点
if(G->matrix[i][j]==1 && !visited[j])
DFSM(G,j);
}
关注我
如果觉得我写的不错,请点个赞 关注我, 您的支持是我更文最大的动力!