1. 程式人生 > >C語言資料結構與演算法之深度、廣度優先搜尋

C語言資料結構與演算法之深度、廣度優先搜尋

一、深度優先搜尋(Depth-First-Search 簡稱:DFS)

1.1 遍歷過程:

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

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

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

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

例無權圖:(預設為字母順序)

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

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

  (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)。

 

二、廣度優先搜尋(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;
}

複製程式碼

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

 

深度和廣度查詢不同之處在於對頂點的訪問順序不同。

第一次寫部落格,應該還是有點問題的(雖然也查了一些資料~.~)

ball ball you can point my errors! thanks a  lot !

 

參考資料:

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

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

鄭州看婦科哪家好

鄭州割包皮醫院

鄭州婦科醫院

鄭州婦科醫院哪家好