C語言數據結構與算法之深度、廣度優先搜索

1、深度優先搜索(Depth-First-Search 簡稱:DFS)算法

1.1 遍歷過程:數組

  (1)從圖中某個頂點v出發,訪問v。數據結構

  (2)找出剛纔第一個被頂點訪問的鄰接點。訪問該頂點。以這個頂點爲新的頂點,重複此步驟,直到訪問過的頂點沒有未被訪問過的頂點爲止。spa

  (3)返回到步驟(2)中的被頂點v訪問的,且還沒被訪問的鄰接點,找出該點的下一個未被訪問的鄰接點,訪問該頂點。設計

  (4)重複(2) (3) 直到每一個點都被訪問過,遍歷結束。3d

例無權圖:(默認爲字母順序)指針

  (1)從頂點A出發,訪問該圖code

  (2)A的鄰接點爲BEF 以B爲頂點開始訪問 B的鄰接點有FDCblog

  (3)B的全部的點均被訪問結束,訪問頂點C 頂點C還有F沒有被訪問遞歸

,結束遍歷。

故遍歷結果爲 A->B->C->D->E->F

有向圖:(默認爲字母順序)

  (1)從頂點A出發,訪問該圖

  (2)A 的出路頂點爲B、D ,從頂點B 開始訪問, B的出路只有E 結束此路;

  (3)開始訪問頂點D,D的出路爲頂點C和F 此時全部頂點都被遍歷了,結束;

故遍歷結果爲: A->B->E->D->C->F

1.2 算法描述

天然語言:從圖中的某個頂點v出發,訪問v,並將visited[v]的值爲true。

      一次檢查v的全部鄰接點w,若是visited[w]的值爲flase,再從w出發進行遞歸遍歷,直到圖中的全部頂點都被訪問過。

僞代碼:

遞歸算法:

  visited[MVNum] <-- false

  count<--v,visited[v]<--true;

  for(w<--FirstAdjVex(G,v);w>=0;w<--NextAdjVex(G,v,w))

    if(!visited[w]  DFS[G,w]); 

採用鄰接矩陣表示:

//輸入圖G(V,E),w表示v的鄰接點

//輸出鄰接矩陣

count<--v; visited[v]<--true;

for(w<--0;w<G.vexnum;w++)

  if( (G.arcs[v][w]!=0)&&(!visited[w])  )

    DFS(G,w);

採用鄰接表:

count<--v; visited[v]<--true;

p<--G.vertices[v].firstarc;

while(p!=NULL) do

  w<--p->adjvex;

  if(!visited[w]) do DFS(G,w)

  p<-- p->nextarc;

1.3用途:檢查圖的連通性和無環性

1.4總結:每一個頂點至多進一次隊列。遍歷圖的過程實質上市經過邊找鄰接點的過程。所以DFS時間複雜度,當用鄰接矩陣表示圖時爲O(n2),其中n爲圖中的頂點數,當以鄰接表作圖的存儲結構時,時間複雜度爲O(e)這裏e爲 圖中的邊數,所以,當以鄰接表爲存儲結構時,DFS時間複雜度爲O(n+e)。

 

2、廣度優先搜索(Breadth-First-Search 簡稱:BFS)

2.1遍歷過程以下:

  (1)從圖中某個頂點v出發,訪問v。

  (2)依次訪問v鄰接各個未訪問過的的全部頂點

  (3)接着從這些鄰接頂點中繼續訪問它們的鄰接頂點,遵循原則 先被訪問的頂點的鄰接點   先於 後被訪問的頂點的鄰接點 被訪問。重複(3)步驟,直至全部的頂點都被訪問過。

這裏的「先被訪問的頂點的鄰接點 」指的是在第二步驟先訪問的頂點而後再先訪問他的鄰接點,包括後來的第三步驟也是這個意思,均爲上一步驟 先訪問的頂點而後再先訪問他的鄰接點。

例:圖仍是上面的那張無權圖

咱們按照字母ABCDEF這樣的順序來排列

(1)以A爲頂點,開始遍歷

(2)A的三個鄰接點BEF 

(3)根據字母順序 從點B開始訪問 B的臨界點有CD 此時,全部的頂點均被訪問

故,遍歷後的結果爲 A ->B-> E-> F-> C-> D

若爲有向圖

(1)根據字母順序,先從頂點A開始訪問

(2)看頂點A的出路,鄰接點爲B,D 。根據字母順序,下一個頂點從B開始

(3)頂點B的出路爲E ,且E沒有出路了,故此路結束

(4)回到和B點同一級的 還有頂點D尚未被訪問 D的出路有兩條,分別爲鄰接點C 和F ,此時全部的頂點都被訪問過。

故 遍歷後的順序爲 A->B->D->E->C->F

2.2算法描述

 天然語言:從圖 中的某個頂點v出發,訪問v,並將visited[v]的值爲true,而後將v進隊

     只要隊列不空,則重複下述處理:

      隊頭頂點u出隊

      依次檢查u的全部鄰接點w,若是visited[w]的值爲false,則訪問w,並將visited[w]的數值爲true,而後將w入隊;

僞代碼: //BFS算法描述

    //輸入:圖G=<V,E>

    //輸出:圖G的BFS遍歷後的前後次序

    visited[v] <--true

    InitQueue(Q);

    EnQueue(Q,v);

    while(!QueueEmpty(Q))  do

      DeQueue(Q,u);

      for(w <--FirstAdjVex(G,u);w>=0;w<--NextAdjVex(G,u,w))

      if(!visited[w]) do

        count<<w; visited[w] <--true;

        EnQueue(Q,w);

2.3用途:計算最短路徑問題

2.4.總結:每一個頂點至多進一次隊列。遍歷圖的過程實質上市經過邊找鄰接點的過程。所以BFS時間複雜度,當用鄰接矩陣表示圖時爲O(n2),其中n爲圖中的頂點數,當以鄰接表作圖的存儲結構時,時間複雜度爲O(e)這裏e爲 圖中的邊數,所以,當以鄰接表爲存儲結構時,BFS時間複雜度爲O(n+e)。

具體的代碼實現以下所示:

#include<stdio.h>
#define N 20
#define TRUE 1
#define FALSE 0
int visited[N];        /*訪問標誌數組*/
typedef struct     /*隊列的定義*/
{
    int data[N];
    int front,rear;
}queue;

typedef struct      /*圖的鄰接矩陣*/
{
    int vexnum,arcnum;
    char vexs[N];
    int arcs[N][N];
}
graph;

void createGraph(graph *g);      /*創建一個無向圖的鄰接矩陣*/
void dfs(int i,graph *g);          /*從第i個頂點出發深度優先搜索*/
void tdfs(graph *g);             /*深度優先搜索整個圖*/
void bfs(int k,graph *g);          /*從第k個頂點廣度優先搜索*/
void tbfs(graph *g);             /*廣度優先搜索整個圖*/
void init_visit();               /*初始化訪問標識數組*/

/*創建一個無向圖的鄰接矩陣*/
void createGraph(graph *g)
{
    int i,j;
    char v;
    g->vexnum=0;
    g->arcnum=0;
    i=0;
    printf("\n輸入頂點序列(以#結束):\n");
    while ((v=getchar())!='#')
    {
        g->vexs[i]=v;            /*讀入頂點信息*/
        i++;
    }
    g->vexnum=i;             /*頂點數目*/
    for (i=0;i<g->vexnum;i++)     /*鄰接矩陣初始化*/
        for (j=0;j<g->vexnum;j++)
            g->arcs[i][j] = 0;/*(1)-鄰接矩陣元素初始化爲0*/
    printf("\n輸入邊的信息(頂點序號,頂點序號),以(-1,-1)結束:\n");
    scanf("%d,%d",&i,&j);      /*讀入邊(i,j)*/
    while (i!=-1)                 /*讀入i爲-1時結束*/
    {
        g->arcs[i][j] = 1;    /*(2)-i,j對應邊等於1*/
        g->arcnum++;
        scanf("%d,%d",&i,&j);
    }
}/* createGraph */

/*(3)---從第i個頂點出發深度優先搜索,補充完整算法*/
void dfs(int i,graph *g)
{
    int j;
    printf("%c", g->vexs[i]);
    visited[i] = TRUE;
    for (j = 0; j < g->vexnum; j++)
        if (g->arcs[i][j] == 1 && !visited[j])
            dfs(j, g);
}/* dfs */

/*深度優先搜索整個圖*/
void tdfs(graph *g)
{
    int i;
    printf("\n從頂點%C開始深度優先搜索序列:",g->vexs[0]);
    for (i=0;i<g->vexnum;i++)
        if (visited[i] != TRUE)   /*(4)---對還沒有訪問過的頂點進行深度優先搜索*/
            dfs(i,g);
    printf("\n");
}/*tdfs*/

/*從第k個頂點廣度優先搜索*/
void bfs(int k,graph *g)
{
    int i,j;
    queue qlist,*q;
    q=&qlist;
    q->rear=0;
    q->front=0;
    printf("%c",g->vexs[k]);
    visited[k]=TRUE;
    q->data[q->rear]=k;
    q->rear=(q->rear+1)%N;
    while (q->rear!=q->front)                 /*當隊列不爲空,進行搜索*/
    {
        i=q->data[q->front];
        q->front=(q->front+1)%N;
        for (j=0;j<g->vexnum;j++)
            if ((g->arcs[i][j]==1)&&(!visited[j]))
            {
                printf("%c",g->vexs[j]);
                visited[j]=TRUE;
                q->data[q->rear] = j;     /*(5)---剛訪問過的結點入隊*/
                q->rear = (q->rear + 1) % N;     /*(6)---修改隊尾指針*/
            }
    }
}/*bfs*/

/*廣度優先搜索整個圖*/
void tbfs(graph *g)
{
    int i;
    printf("\n從頂點%C開始廣度優先搜索序列:",g->vexs[0]);
    for (i=0;i<g->vexnum;i++)
        if (visited[i]!=TRUE)
            bfs(i,g);                        /*從頂點i開始廣度優先搜索*/
    printf("\n");
}/*tbfs*/

void init_visit()  /*初始化訪問標識數組*/
{
    int i;
    for (i=0;i<N;i++)
        visited[i]=FALSE;
}

int main()
{
    graph ga;
    int i,j;
    printf("***********圖鄰接矩陣存儲和圖的遍歷***********\n");
    printf("\n1-輸入圖的基本信息:\n");
    createGraph(&ga);
    printf("\n2-無向圖的鄰接矩陣:\n");
    for (i=0;i<ga.vexnum;i++)
    {
        for (j=0;j<ga.vexnum;j++)
            printf("%3d",ga.arcs[i][j]);
        printf("\n");
    }
    printf("\n3-圖的遍歷:\n");
    init_visit(); /*初始化訪問數組*/
    tdfs(&ga);    /*深度優先搜索圖*/
    init_visit();
    tbfs(&ga);    /*廣度優先搜索圖*/
    return 0;
}

運行結果(輸入的爲本節中一直用到的無向圖)

 

深度和廣度查找不一樣之處在於對頂點的訪問順序不一樣。

第一次寫博客,應該仍是有點問題的(雖然也查了一些資料~.~)

 

 

參考資料:

《數據結構(C語言版)》  嚴蔚敏 李冬梅 吳偉民著 人民郵電出版社

《程序設計中實用的數據結構 》  王建德 吳永輝著  人民郵電出版社

相關文章
相關標籤/搜索