(1.2.6.1)图的遍历--深度优先、广度优先

遍历

    图的遍历是指从图中的某一顶点出发,按照一定的策略访问图中的每一个顶点。当然,每个顶点有且只能被访问一次。

    在图的遍历中,深度优先和广度优先是最常使用的两种遍历方式。这两种遍历方式对无向图和有向图都是适用的,并且都是从指定的顶点开始遍历的。先看下两种遍历方式的遍历规则:

(1)深度优先

1)定义    

深度优先遍历也叫深度优先搜索(Depth First Search)。它的遍历规则:不断地沿着顶点的深度方向遍历。顶点的深度方向是指它的邻接点方向。

    具体点,给定一图G=<V,E>,用visited[i]表示顶点i的访问情况,则初始情况下所有的visited[i]都为false。假设从顶点V0开始遍历,则下一个遍历的顶点是V0第一个邻接点Vi,接着遍历Vi第一个邻接点Vj,……直到所有的顶点都被访问过。

    所谓的第一个是指在某种存储结构中(邻接矩阵邻接表),所有邻接点中存储位置最近的,通常指的是下标最小的。在遍历的过程中有两种情况经常出现

  1. 某个顶点的邻接点都已被访问过的情况,此时需回溯已访问过的顶点。
  2. 图不连通,所有的已访问过的顶点都已回溯完了,仍找不出未被访问的顶点。此时需从下标0开始检测visited[i],找到未被访问的顶点i,从i开始新一轮的深度搜索。

看一个例子
《(1.2.6.1)图的遍历--深度优先、广度优先》

从V
0
开始遍历
    遍历分析:V0有两个邻接点V1V2,选择下标最小的V1遍历。接着从V1开始深度遍历,V1只有邻接点V3,也就是没有选的:遍历V3。接着从V3开始遍历,V3只有邻接点V0,而V0已经被遍历过。此时出现了上面提到的情况一,开始回溯V1V1无未被遍历的邻接点,接着回溯V0,V0有一个未被遍历的邻接点V2,新的一轮深度遍历从V2开始。V2无邻接点,且无法回溯。此时出现了情况二,检测visited[i],只有V4了。深度遍历完成。看到回溯,应该可以想到需要使用栈。
遍历序列是
V0->V1->V3->V2->V4
从其它顶点出发的深度优先遍历序列是:
V1->V3->V0->V2->V4
V2->V0->V1->V3->V4

V3->V0->V1->V2->V4

V4->V2->V0->V1->V3

2)算法实现

1.矩阵

void DFS(MGraph G,int v)      //深度优先搜索
{
    int i;
    printf("%d ",v);          //访问结点v
    visited[v]=true;
    for(i=0;i<G.n;i++)       //访问与v相邻的未被访问过的结点
    {
        if(G.edges[v][i]!=0&&visited[i]==false)
        {
            DFS(G,i);
        }
    }
}
void DFS1(MGraph G,int v)   //非递归实现
{
    stack<int> s;
    printf("%d ",v);        //访问初始结点
    visited[v]=true;
    s.push(v);              //入栈
    while(!s.empty())
    {
        int i,j;
        i=s.top();          //取栈顶顶点
        for(j=0;j<G.n;j++)  //访问与顶点i相邻的顶点
        {
            if(G.edges[i][j]!=0&&visited[j]==false)
            {
                printf("%d ",j);     //访问
                visited[j]=true;
                s.push(j);           //访问完后入栈
                break;               //找到一个相邻未访问的顶点,访问之后则跳出循环
            }
        }
        if(j==G.n)                   //如果与i相邻的顶点都被访问过,则将顶点i出栈
            s.pop();
    }
}

2.邻接表

(2)广度优先

1)定义

    广度优先遍历也叫广度优先搜索(Breadth First Search)。它的遍历规则:

  1. 先访问完当前顶点的所有邻接点。(应该看得出广度的意思)
  2. 先访问顶点的邻接点先于后访问顶点的邻接点被访问。

    具体点,给定一图G=<V,E>,用visited[i]表示顶点i的访问情况,则初始情况下所有的visited[i]都为false。假设从顶点V0开始遍历,且顶点V0的邻接点下表从小到大有Vi、Vj…Vk。按规则1,接着应遍历Vi、Vj和Vk。再按规则2,接下来应遍历Vi的所有邻接点,之后是Vj的所有邻接点,…,最后是Vk的所有邻接点。接下来就是递归的过程…
在广度遍历的过程中,会出现图不连通的情况,此时也需按上述情况二来进行:测试visited[i]…。
在上述过程中,可以看出需要用到队列。


举个例子,还是同样一幅图:
《(1.2.6.1)图的遍历--深度优先、广度优先》

从V
0
开始遍历
    遍历分析:V0有两个邻接点V1和V2,于是按序遍历V1、V2。V1先于V2被访问,于是V1的邻接点应先于V2的邻接点被访问,那就是接着访问V3。V2无邻接点,只能看V3的邻接点了,而V0已被访问过了。此时需检测visited[i],只有V4了。广度遍历完毕。

遍历序列是

V0->V1->V2->V3->V4
从其它顶点出发的广度优先遍历序列是
V1->V3->V0->V
2
->
V
4

V2->V0->V1->V3->V4

V3->V0->V1->V2->V4

V4->V2->V0->V1->V3

2)算法实现


     1.矩阵存储

void BFS(MGraph G,int v)      //广度优先搜索
{
    queue<int> Q;             //STL模板中的queue
    printf("%d ",v);
    visited[v]=true;
    Q.push(v);
    while(!Q.empty()) 
    {
        int i,j;
        i=Q.front();         //取队首顶点
        Q.pop();
        for(j=0;j<G.n;j++)   //广度遍历
        {
            if(G.edges[i][j]!=0&&visited[j]==false)
            {
                printf("%d ",j);
                visited[j]=true;
                Q.push(j);
            }
        }
    }
}

 2.邻接表

  1. void Link_GP<T1,T2>::bfs_Link_GP()  
  2. {  
  3.     int *mark,k;  
  4.     sq_Queue<int> q(nn); //建立循环队列空间并初始化  
  5.     node<T1> *p;  
  6.     mark=new int[nn];   //申请标志数组空间  
  7.     for(k=0;k<nn;k++)  
  8.         mark[k]=0;  
  9.     for(k=0;k<nn;k++)   //对每个头结点横向优先搜索  为防止孤点
  10.     {  
  11.         if(mark[k]==0)  //当前未查访过  
  12.         {  
  13.             mark[k]=1;   //记录当前结点已经查访过  
  14.             cout<<gp->data<<” “//输出当前值  
  15.             q.ins_sq_Queue(k);   //当前结点编号入队  
  16.             while(q.flag_sq_Queue())//队列不空  
  17.             {  
  18.                 k=q.del_sq_Queue(); //从队列中退出一个结点作为当前结点  
  19.                 p=(gp+k)->link;// 所有后件结点链表指针  
  20.                 while(p!=NULL) //还有后件  
  21.                 {  
  22.                     k=p->num-1;  
  23.                     if(mark[k]==0)  
  24.                     {  
  25.                         cout<<(gp+k)->data<<”  “;  
  26.                         mark[k]=1;  
  27.                         q.ins_sq_Queue(k);  
  28.                     }  
  29.                     p=p->next;  //下一个后件  
  30.                 }  
  31.             }  
  32.         }  
  33.     }  
  34. cout<<endl;  
  35. delete mark;  
  36. return;  

    原文作者:数据结构之图
    原文地址: https://blog.csdn.net/fei20121106/article/details/44260699
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞