基本概念
从给定图中任意指定的顶点(称为初始点)出发,按照某种搜索方法沿着图的边访问图中的所有顶点,使每个顶点仅被访问一次,这个过程称为图的遍历。如果给定图是连通的无向图或者是强连通的有向图,则遍历过程一次就能完成,并可按访问的先后顺序得到由该图所有顶点组成的一个序列。
根据搜索方法的不同,图的遍历方法有两种:一种叫做深度优先搜索法(DFS);另一种叫做广度优先搜索法(BFS)。
深度优先搜索
深度优先搜索遍历类似于树的先序遍历。假定给定图G的初态是所有顶点均未被访问过,在G中任选一个顶点i作为遍历的初始点,则深度优先搜索遍历可定义如下:、
①首先访问顶点i,并将其访问标记置为访问过,即visited[i]=1;
②然后搜索与顶点i有边相连的下一个顶点j,若j未被访问过,则访问它,并将j的访问标记置为访问过,visited[j]=1,然后从j开始重复此过程,若j已访问,再看与i有边相连的其它顶点;
③若与i有边相连的顶点都被访问过,则退回到前一个访问顶点并重复刚才过程,直到图中所有顶点都被访问完止。
深搜的邻接矩阵实现:
代码:
// 输入
int N, M;
char field[MAX_N][MAX_M + 1]; // 图
// 现在位置(x,y)
void dfs(int x, int y) {
// 将现在所在位置替换为.
field[x][y] = '.';
// 循环遍历移动的8个方向
for (int dx = -1; dx <= 1; dx++) {
for (int dy = -1; dy <= 1; dy++) {
// 向x方向移动dx,向y方向移动dy,移动的结果为(nx,ny)
int nx = x + dx, ny = y + dy;
// 判断(nx,ny)是不是在园子内,以及是否有积水
if (0 <= nx && nx < N && 0 <= ny && ny < M && field[nx][ny] == 'W') dfs(nx, ny);
}
}
return ;
}
void solve() {
int res = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
if (field[i][j] == 'W') {
// 从有W的地方开始dfs
dfs(i, j);
res++;
}
}
}
printf("%d\n", res);
}
邻接表实现:
代码1:
//返回v的第一个邻接顶点的序号,若顶点在G中没有邻接顶点,则返回-1
int FirstAdjVex(MGraph G,string V)
{
int i=LocateVex(G,V); // i为顶点V在图G中的序号
for(int j=0;j<G.vexnum;++j)
if(G.arcs[i][j]!=0 and G.arcs[i][j]!=INF)
return j;
return -1;
}
//图G存在,V1是G中某个顶点,V2是V1的邻接顶点
//操作结果: 返回V1的(相对于V2的)下一个邻接顶点的序号,
//若V2是V1的最后一个邻接顶点,则返回-1
int NextAdjVex(MGraph G,string V1,string V2)
{
int k1,k2;
k1=LocateVex(G,V1); // k1为顶点V1在图G中的序号
k2=LocateVex(G,V2); // k2为顶点V2在图G中的序号
for(int i=k2+1;i<G.vexnum;i++)
if(G.arcs[k1][i]!=0 and G.arcs[k1][i]!=INF)
return i;
return -1;
}
bool visited[MAX_VERTEX_NUM]; //访问标志数组(全局变量)
void DFS(MGraph G,int v) // 从第v个顶点出发递归地深度优先遍历图G
{
string V1=G.vexs[v];
visited[v]=true; // 设置访问标志为true(已访问)
cout<<G.vexs[v]<<' '; // 访问第v个顶点
for(int w=FirstAdjVex(G,V1); w>=0; w=NextAdjVex(G,V1,G.vexs[w]))
if(!visited[w])
DFS(G,w); // 对v的尚未访问的序号为w的邻接顶点递归调用DFS
}
//从第1个顶点起,深度优先遍历图G,并对每个顶点输出顶点的值一次且仅一次
void DFSTraverse(MGraph G)
{
for(int v=0;v<G.vexnum;v++)
visited[v]=false; // 访问标志数组初始化(未被访问)
for(int v=0;v<G.vexnum;v++)
if(!visited[v])
DFS(G,v); //对尚未访问的顶点调用DFS
cout<<endl;
}
代码2:
void DFS(ALGraph *G,int v)
{ ArcNode *p; int w;
visited[v]=1; //置已访问标记
printf("%d ",v); //输出被访问顶点的编号
p=G->adjlist[v].firstarc;
//p指向顶点v的第一条边的边头节点
while (p!=NULL)
{ w=p->adjvex;
if (visited[w]==0)
DFS(G,w); //若w顶点未访问,递归访问它
p=p->nextarc;
//p指向顶点v的下一条边的边头节点
}
}
附一篇ACM试题

代码:
// 输入
int a[MAX_N];
int n, k;
// 已经从前i项得到了和sum,然后对于i项之后的进行分支
bool dfs(int i, int sum) {
// 如果前n项都计算过了,则返回sum是否与k相等
if (i == n) return sum == k;
// 不加上a[i]的情况
if (dfs(i + 1, sum)) return true;
// 加上a[i]的情况
if (dfs(i + 1, sum + a[i])) return true;
// 无论是否加上a[i]都不能凑成k就返回false
return false;
}
void solve() {
if (dfs(0, 0)) printf("Yes\n");
else printf("No\n");
}
广度优先搜索
广度优先搜索遍历类似于树的按层次遍历。设图G的初态是所有顶点均未访问,在G 中任选一顶点i作为初始点,则广度优先搜索的基本思想是:
①首先访问顶点i,并将其访问标志置为已被访问,即visited[i]=1;
②接着依次访问与顶点i有边相连的所有顶点W1,W2,…,Wt;
③然后再按顺序访问与W1,W2,…,Wt有边相连又未曾访问过的顶点;
依此类推,直到图中所有顶点都被访问完为止。
代码1:
//从第1个顶点起,按广度优先非递归遍历图G
//使用数组Q来实现队列操作
//使用标志数组visited对访问进行标记
void BFSTraverse(MGraph G)
{
string V;
string Q[50];
int front=0,rear=0;
for(int i=0;i<G.vexnum;++i)
visited[i]=false; //标志数组置初值
for (int i=0;i<G.vexnum;i++)
if (!visited[i]) // i尚未访问
{
visited[i]=true; // 设置访问标志为true(已访问)
cout<<G.vexs[i]<<' ';
Q[rear]=G.vexs[i]; // v入队列
++rear;
while (front!=rear)
{
V=Q[front]; // 队头元素出队并置为V
++front;
for (int i=FirstAdjVex(G,V); i>=0; i=NextAdjVex(G,V,G.vexs[i]))
if (!visited[i]) // i为V的尚未访问的邻接顶点的序号
{
visited[i]=true;
cout<<G.vexs[i]<<' ';
Q[rear]=G.vexs[i];
++rear;
}
}
}
cout<<endl;
}
代码2(循环队列):
void BFS(ALGraph *G,int v)
{ ArcNode *p; int w,i;
int queue[MAXV],front=0,rear=0; //定义循环队列
int visited[MAXV]; //定义存放节点的访问标志的数组
for (i=0;i<G->n;i++) visited[i]=0; //访问标志数组初始化
printf("%2d",v); //输出被访问顶点的编号
visited[v]=1; //置已访问标记
rear=(rear+1)%MAXV;
queue[rear]=v; //v进队
while (front!=rear) //若队列不空时循环
{ front=(front+1)%MAXV;
w=queue[front]; //出队并赋给w
p=G->adjlist[w].firstarc; //找w的第一个的邻接点
while (p!=NULL)
{ if (visited[p->adjvex]==0)
{ printf(“%2d”,p->adjvex); //访问之
visited[p->adjvex]=1;
rear=(rear+1)%MAXV; //该顶点进队
queue[rear]=p->adjvex;
}
p=p->nextarc; //找下一个邻接顶点
}
}
printf("\n");
}
图的遍历的应用
判断图是否连通
从图上的任意点出发,如能通过深搜遍历完所有顶点,则图为连通图。
代码:
bool Connect(ALGraph *G) //判断无向图G的连通性
{ int i;
bool flag=true;
for (i=0;i<G->n;i++) //visited数组置初值
visited[i]=0;
DFS(G,0); //调用前面的中DSF算法,从顶点0开始深度优先遍历
for (i=0;i<G->n;i++)
if (visited[i]==0)
{ flag=false;
break;
}
return flag;
}
判断图上是否有简单路径
从顶点u开始进行深度优先搜索,当搜索到顶点v时表明从顶点u到顶点v有路径。
代码:
int visited[MAXV]={0}; //全局变量
void isPath(ALGraph *G,int u,int v,bool &flag)
//flag表示uv是否有路径,初始时flag=false
{ int w; ArcNode *p;
visited[u]=1;
p=G->adjlist[u].firstarc; //p指向u的第一条边
while (p!=NULL)
{ w=p->adjvex; //w为u的邻接顶点
if (w==v)
{ flag=true;
return;
}
else if (visited[w]==0) //若顶点未标记访问,则递归访问之
isPath(G,w,v,has); //从顶点w出发继续查找
p=p->nextarc //找u的下一个邻接顶点
}
}
输出图上的一条简单路径
采用深度优先遍历的方法。为此在深度优先遍历算法的基础上增加v、path和d三个形参,其中path存放顶点u到v的路径,d表示path中的路径长度,其初值为-1。当从顶点u遍历到顶点v后,输出path并返回。
代码:
void FindaPath(AGraph *G,int u,int v,int path[],int d)
{ //d表示path中的路径长度,初始为-1
int w,i; ArcNode *p;
visited[u]=1;
d++; path[d]=u; //路径长度d增1,顶点u加入到路径中
if (u==v) //找到一条路径后输出并返回
{ printf("一条简单路径为:");
for (i=0;i<=d;i++) printf("%d ",path[i);
printf("\n");
return; //找到一条路径后返回
}
p=G->adjlist[u].firstarc; //p指向顶点u的第一个相邻点
while (p!=NULL)
{ w=p->adjvex; //相邻点的编号为w
if (visited[w]==0)
FindaPath(G,w,v,path,d);
p=p->nextarc; //p指向顶点u的下一个相邻点
}
}
输出图上的所以简单路径
所谓简单路径是指路径上的顶点不重复。利用回溯的深度优先搜索方法。从顶点u开始进行深度优先搜索,在搜索过程中,需要把当前的搜索线路记录下来。为此设立一个数组path保存走过的路径,用d记录走过的路径长度。若当前扫描到的顶点u等于v时,表示找到了一条路径,则输出路径path。
代码:
void PathAll(ALGraph *G,int u,int v,int path[],int d)
//d是到当前为止已走过的路径长度,调用时初值为-1
{ int w,i; ArcNode *p;
visited[u]=1; d++; //路径长度增1
path[d]=u; //将当前顶点添加到路径中
if (u==v && d>1) //输出一条路径
{ printf(" ");
for (i=0;i<=d;i++) printf("%d ",path[i]);
printf("\n");
}
p=G->adjlist[u].firstarc; //p指向u的第一条边
while (p!=NULL)
{ w=p->adjvex; //w为u的邻接顶点
if (visited[w]==0) //若顶点未标记访问,则递归访问之
PathAll(G,w,v,path,d);
p=p->nextarc //找u的下一个邻接顶点
}
visited[u]=0; //恢复环境
}
void main()
{ int path[MAXV],u=1,v=4,i,j;
MGraph g;
ALGraph *G;
g.n=5;g.e=6;
int A[MAXV][MAXV]={ {0,1,0,1,0},{1,0,1,0,0},
{0,1,0,1,1}, {1,0,1,0,1}, {0,0,1,1,0} };
for (i=0;i<g.n;i++) //建立图的邻接矩阵
for (j=0;j<g.n;j++)
g.edges[i][j]=A[i][j];
MatToList(g,G);
for (i=0;i<g.n;i++)
visited[i]=0;
printf("图G:\n");DispAdj(G); //输出邻接表
printf("从%d到%d的所有路径:\n",u,v);
PathAll(G,u,v,path,-1);
printf("\n");
}

965

被折叠的 条评论
为什么被折叠?



