1. 程式人生 > >無向圖-鄰接連結串列的深度優先遍歷-DFS

無向圖-鄰接連結串列的深度優先遍歷-DFS

一、DFS思想

本演算法以無向網為例,儲存方式採用鄰接連結串列

1)將該網以鄰接連結串列的方式儲存
2)選取A點為起始點,訪問此頂點,用一個visit的bool型陣列記錄訪問狀態(false表示未被訪問,true表示已訪問)
3)從A的未被訪問的鄰接點出發,深度優先遍歷圖,直到圖中所有和v有路徑相通的頂點都被訪問到


二、演算法複雜度:O(n+e)

        鄰接矩陣和鄰接表都是實現BFS和DFS的方法,鄰接矩陣時間複雜度為O(n^2),鄰接表的時間複雜度為O(n+e)。因此鄰接矩陣適用於稠密圖,鄰接表適用於稀疏圖。


三、在實際編寫程式碼時,筆者發現

1)對採用鄰接連結串列作為儲存方式的圖做DFS時,由於建立鄰接連結串列時可採用不同的插入方法:比如頭插法和尾插法,會使得做DFS時相同的圖結構會有不一樣的結果;

2)即使相同的圖結構,採用不同的儲存方式(鄰接矩陣和鄰接連結串列)做DFS時,它們的結果有時也會不一樣;

筆者認為這都是正常現象,DFS的思想還是沒變。

四、測試用例圖

本演算法的測試用例為《大話資料結構》p239中的圖7-5-2。如下圖所示:


五、C程式碼實現

/*******************************************************************************************
【DFS】
Author:tmw
date:2018-2-20
********************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>


#define MAX_VERTEX 100
#define inf 65535  //表示兩點之間沒有邊相連

int visit[MAX_VERTEX];   //標記頂點是否被訪問

/**無向圖的鄰接連結串列的建立**/
//邊表結點資料結構
typedef struct EdgeNode
{
    int adjvex;  //儲存該頂點對應的下標
    int weight;
    struct EdgeNode *next; //指向下一個鄰接點
}EdgeNode;

//頂點表結點資料結構
typedef struct VertexNode
{
    char Vertex;  //儲存頂點資訊
    EdgeNode *FistEdge; //邊表頭指標
}VertexNode,AdjList[MAX_VERTEX];

//鄰接連結串列圖的資料結構
typedef struct
{
    AdjList adjList;
    int VertexNumber,EdgeNumber; //頂點數和邊數
}GraphAdjList;


/**無向圖鄰接連結串列的建立**/
void Create_no_direction_LinkList_Graph(GraphAdjList *G)
{
    int i,j,w,k;
    printf("請輸入無向圖鄰接連結串列的頂點數和邊數:\n");
    scanf("%d %d",&G->VertexNumber,&G->EdgeNumber);

    //輸入頂點資訊,建立頂點表
    printf("頂點表的建立:輸入頂點資訊,如ABCDEF.....\n");
    char ch;
    while( ( ch = getchar()  != '\n' ) );
    for(i=0;i<G->VertexNumber;i++)
    {
        scanf("%c",&G->adjList[i].Vertex);
        G->adjList[i].FistEdge = NULL;
    }
    printf("邊表的建立:輸入邊(vi,vj)的頂點下標,權值統一為1,如:0 1 1(權值)\n");

    for(k=0;k<G->EdgeNumber;k++)
    {
        scanf("%d %d %d",&i,&j,&w);

        EdgeNode *e;
        e = (EdgeNode*)malloc(sizeof(EdgeNode));
        e->weight = w;
        e->adjvex = j;
        e->next = G->adjList[i].FistEdge;   //頭插法將下標為j的頂點插入與之相連的下標為i的結點連結串列中
        G->adjList[i].FistEdge = e;

        //無向圖,因此是對稱的,同樣的操作將下標為i的頂點插入與之相連的下標為j的結點的連結串列中
        e = (EdgeNode*)malloc(sizeof(EdgeNode));
        e->weight = w;
        e->adjvex = i;
        e->next = G->adjList[j].FistEdge;
        G->adjList[j].FistEdge = e;
    }


//    列印檢查
    printf("---------------------構造出來的無向圖鄰接連結串列的邊資訊如下---------------------\n");
    for(i=0;i<G->VertexNumber;i++)
    {
        EdgeNode *p;
        p = G->adjList[i].FistEdge;
        printf("%d\t",i);
        while(p != NULL)
        {
            printf("%d ",p->adjvex);
            p = p->next;
        }
        printf("\n");
    }
}

//無向圖的鄰接連結串列的第i個頂點的DFS
void DFS(GraphAdjList G, int i)
{
    visit[i] = true;
    printf("%c ",G.adjList[i].Vertex);

    EdgeNode *p;
    p = G.adjList[i].FistEdge;

    while(p)
    {
        if( !visit[p->adjvex] )
            DFS(G,p->adjvex);
        p = p->next;
    }
}

//對整個無向圖進行DFS
void DFS_Travel(GraphAdjList G)
{
    int i;

    //先初始化visit陣列
    for(i=0;i<G.VertexNumber;i++)
        visit[i] = false;

    printf("無向圖的鄰接連結串列DFS結果為:\n");
    for(i=0;i<G.VertexNumber;i++)
        if(!visit[i])
            DFS(G,i);
}

六、測試程式碼及測試結果

int main()
{
    printf("測試程式碼\n");
    GraphAdjList G;
    Create_no_direction_LinkList_Graph(&G);
    DFS_Travel(G);
    return 0;
}

測試結果如下: