图的遍历

基本概念

从给定图中任意指定的顶点(称为初始点)出发,按照某种搜索方法沿着图的边访问图中的所有顶点,使每个顶点仅被访问一次,这个过程称为图的遍历。如果给定图是连通的无向图或者是强连通的有向图,则遍历过程一次就能完成,并可按访问的先后顺序得到由该图所有顶点组成的一个序列。

根据搜索方法的不同,图的遍历方法有两种:一种叫做深度优先搜索法(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表示uv是否有路径,初始时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");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值