1. 程式人生 > >資料結構----BFS和DFS詳解

資料結構----BFS和DFS詳解

前言
The art of teaching is the art of assisting discovery.
Name:Willam
Time:2017/2/28

這篇部落格將會介紹兩種遍歷圖的演算法,一種是:DFS—-深度優先搜尋,另外一種就是:BFS–廣度優先搜尋。

1、DFS (深度優先搜尋)

演算法思路:
從頂點V開始,訪問這個頂點,然後依次從V的未被訪問的鄰接點出發深度優先遍歷圖,直至圖中所有和V有路徑的相通的頂點都被訪問了,如果此時還有頂點未被訪問,則選擇圖中未被訪問的那個頂點作為起點,重複上述動作。

具體的程式碼實現如下:

#include<iostream>
#include<string> using namespace std; //使用鄰接矩陣完成圖的遍歷 struct Graph_array { int vexnum; //圖的頂點數 int edge; //圖的邊數 int ** arc; //鄰接矩陣 int kind; //0,為有向圖,1,為無向圖 string * infromation; //表示每個頂點的資訊 }; //使用鄰接矩陣表示的圖 void createGraph_by_array(int **edge,Graph_array & g) { int i = 0
; g.arc = new int*[g.vexnum];//為鄰接矩陣開闢空間 for (i = 0; i < g.vexnum; i++) { g.arc[i] = new int[g.vexnum]; for (int j = 0; j < g.vexnum; j++) g.arc[i][j] = 0; } for (i = 0; i < g.edge; i++) { //對矩陣進行賦值 g.arc[edge[i][0] - 1][edge[i][1
] - 1] = 1; } } //列印鄰接矩陣 void print_array(Graph_array g) { int i = 0; for (i = 0; i <g.vexnum; i++) { //cout << g.infromation[i] << " "; for (int j = 0; j < g.vexnum; j++) { cout << g.arc[i][j] << " "; } cout << endl; } } //進行DFS遍歷, void DFS_store_array(Graph_array g,int v,bool * & visit) { cout << g.infromation[v] << " "; visit[v] = true; for (int i = 0; i < g.vexnum; i++) {//找出下一個位被訪問的頂點 if (g.arc[v][i] == 0 || g.arc[v][i] == INT_MAX) { //如果兩個頂點不存在邊 continue; } else if (!visit[i]) {//如果沒有被訪問,訪問該結點 DFS_store_array(g, i, visit); } } } //呼叫對應的DFS函式進行圖的遍歷 void DFS_array_travel(Graph_array g,int begin) { bool * visit; visit = new bool[g.vexnum]; int i; for (i = 0; i < g.vexnum; i++) { visit[i] = false; } cout << "圖的DFS遍歷結果:" << endl; DFS_store_array(g,begin - 1,visit); //如果圖是非聯通同,那麼我們這裡還需要對每個頂點遍歷一次,保證 //全部頂點都被訪問了 for (i = 0; i < g.vexnum; i++) { if (!visit[i]) DFS_store_array(g, i,visit); } } //使用鄰接表表示圖進行圖的遍歷 //表結點 struct ArcNode { int adfvex;//表示該邊的另外一個頂點在頂點表中的下標 ArcNode * next; //表示依附在該頂點的下一條邊的資訊 }; //頭結點 struct Vnode { string data; //記錄基本資訊i ArcNode * firstarc;//記錄第一條依附在該頂點的邊 }; //一個圖的結構 struct Graph_List { int vexnum; //圖的頂點數 int edge; //圖的邊數 Vnode * node; //鄰接表 int kind; //0,為有向圖,1,為無向圖 }; //建立鄰接表 void createGraph_list(Graph_List & g, int **edge) { int i; for (i = 0; i < g.edge; i++) { ArcNode * next=new ArcNode; next->adfvex=edge[i][1]-1; next->next = NULL; //判斷該頂點的是否已經有邊依附 if (g.node[edge[i][0]-1].firstarc == NULL) { g.node[edge[i][0]-1].firstarc = next; } else {//尋找連結串列的最後一個結點 ArcNode * now; now = g.node[edge[i][0]-1].firstarc; while (now->next) { now = now->next; } now->next = next; } } } //列印鄰接表 void print_list(Graph_List g) { int i; for (i = 0; i < g.vexnum; i++) { cout << g.node[i].data << " "; ArcNode * now; now = g.node[i].firstarc; while (now) { cout << now->adfvex << " "; now = now->next; } cout << endl; } } //使用DFS進行遍歷圖,在鄰接表的情況下進行遍歷 void DFS_store_list(Graph_List g, int v, bool * & visit) { cout << g.node[v].data << " "; visit[v] = true; ArcNode * next = g.node[v].firstarc; while (next) { if (!visit[next->adfvex]) { DFS_store_list(g, next->adfvex, visit);//遞迴 } else { next = next->next; } } } //呼叫上面那個函式進行圖的遍歷 void DFS_list(Graph_List g, int begin) { int i; bool * visit = new bool[g.vexnum]; for (i = 0; i < g.vexnum; i++) { visit[i] = false; } cout << "圖的DFS遍歷結果:" << endl; DFS_store_list(g, begin - 1, visit); for (i = 0; i < g.vexnum; i++) { if(!visit[i]) DFS_store_list(g, i, visit); } } int main() { Graph_array g; Graph_List G; int i; cout << "輸入圖的種類:" << endl; cin >> g.kind; G.kind = g.kind; cout << "輸入圖的頂點個數" << endl; cin >> g.vexnum; G.vexnum = g.vexnum; cout << "輸入圖的邊的個數(輸入時注意,無向圖的邊要比看的邊乘以2,然後輸入的記得把重複的邊也要輸進去)" << endl; cin >> g.edge; G.edge = g.edge; g.infromation = new string[g.vexnum]; G.node = new Vnode[G.vexnum]; cout << "輸入每個頂點資訊(如名稱):" << endl; for (i = 0; i < g.vexnum; i++) { cin >> g.infromation[i]; G.node[i].data = g.infromation[i]; G.node[i].firstarc = NULL; } int ** edge_information; edge_information = new int*[g.edge]; cout << "輸入每條邊兩個頂點的編號:" << endl; for (i = 0; i < g.edge; i++) { edge_information[i] = new int[2]; cin >> edge_information[i][0]; cin >> edge_information[i][1]; } int **arc; //鄰接矩陣 //構造鄰接矩陣,其中最後一次引數:1,代表無向圖,0,代表有向圖 createGraph_by_array(edge_information,g); cout << "圖的鄰接矩陣為:" << endl; print_array(g); cout << endl; DFS_array_travel(g, 1); cout << endl; createGraph_list(G, edge_information); cout << "圖的鄰接表為:" << endl; print_list(G); cout << endl; DFS_list(G, 1); cout << endl; system("pause"); return 0; }

下面,我們對如下這個圖進行遍歷,
這裡寫圖片描述
使用上述程式,輸出的結果為:
(輸入時注意,無向圖的邊要比看的邊乘以2,然後輸入的記得把重複的邊也要輸進去)
這裡寫圖片描述

2、BFS (廣度優先搜尋)

BFS就是我們所說的廣度優先搜尋,它的思路就是:假設從圖中的頂點V出,在訪問了v之後,依次訪問v的各個未被訪問的鄰接點,然後,分別從這些鄰接點出發,依次訪問他們的鄰接點,並使“先被訪問的頂點的鄰接點”先於“後被訪問的鄰接點”先被訪問,直至圖中所有的頂點都被訪問到為止,防止出現非連通圖的情況,我們需要最後遍歷一遍,看是否所有的點都被訪問了,如果有未被訪問的點,那麼就把該點作為一個新的起點。

程式碼實現如下:

#include<iostream>
#include<string>
#include<queue>
using namespace std;

//使用鄰接矩陣完成圖的遍歷
struct Graph_array {
    int vexnum;  //圖的頂點數
    int edge;    //圖的邊數
    int ** arc;  //鄰接矩陣
    int kind;    //0,為有向圖,1,為無向圖
    string * infromation; //表示每個頂點的資訊

};
//使用鄰接矩陣表示的圖
void createGraph_by_array(int **edge, Graph_array & g) {
    int i = 0;
    g.arc = new int*[g.vexnum];//為鄰接矩陣開闢空間
    for (i = 0; i < g.vexnum; i++)
    {
        g.arc[i] = new int[g.vexnum];
        for (int j = 0; j < g.vexnum; j++)
            g.arc[i][j] = 0;
    }
    for (i = 0; i < g.edge; i++)
    {
        //對矩陣進行賦值
        g.arc[edge[i][0] - 1][edge[i][1] - 1] = 1;
    }
}
//列印鄰接矩陣
void print_array(Graph_array g) {

    int i = 0;
    for (i = 0; i <g.vexnum; i++) {
        //cout << g.infromation[i] << " ";
        for (int j = 0; j < g.vexnum; j++) {
            cout << g.arc[i][j] << " ";
        }
        cout << endl;
    }
}

//呼叫對應的BFS函式進行圖的遍歷
void BFS_array_travel(Graph_array g, int begin) {
    bool * visit;
    visit = new bool[g.vexnum];
    int i;
    for (i = 0; i < g.vexnum; i++) {
        visit[i] = false;
    }
    cout << "圖的BFS遍歷結果:" << endl;
    //通過我們之前說的演算法思路,我們可以知道
    //我們需要使用先進先出的資料儲存結構實現我們的BFS,其實那就是佇列
    queue<int>  q;
    for (int v = 0; v < g.vexnum; v++) {//這重迴圈是為了保證非連通同的情況下,每個頂點都可以被訪問
        if (!visit[(begin-1 + v) % g.vexnum])//注意起點不一定是v1
        {
            cout << g.infromation[(begin - 1 + v) % g.vexnum] << " ";
            visit[(begin - 1 + v) % g.vexnum] = true;
            q.push((begin - 1 + v) % g.vexnum);//初始化我們的佇列
            while (!q.empty())
            {
                int u = q.front();
                q.pop();
                for (int j = 0; j < g.vexnum; j++) {
                    if (g.arc[u][j] == 0 || g.arc[u][j] == INT_MAX) { //如果兩個頂點不存在邊
                        continue;
                    }
                    else if (!visit[j] ) {//先訪問所有和u相連的頂點,並且把它們加入佇列
                        cout << g.infromation[j] << " ";
                        visit[j] = true;
                        q.push(j);
                    }
                }
            }
        }

    }
    cout << "完成" << endl;

}

//使用鄰接表表示圖進行圖的遍歷
//表結點
struct ArcNode {
    int adfvex;//表示該邊的另外一個頂點在頂點表中的下標
    ArcNode * next; //表示依附在該頂點的下一條邊的資訊
};
//頭結點
struct Vnode {
    string data; //記錄基本資訊i
    ArcNode * firstarc;//記錄第一條依附在該頂點的邊
};
//一個圖的結構
struct Graph_List {
    int vexnum;  //圖的頂點數
    int edge;    //圖的邊數
    Vnode * node;  //鄰接表
    int kind;    //0,為有向圖,1,為無向圖
};
//建立鄰接表
void createGraph_list(Graph_List & g, int **edge) {
    int i;
    for (i = 0; i < g.edge; i++)
    {
        ArcNode * next = new ArcNode;
        next->adfvex = edge[i][1] - 1;
        next->next = NULL;
        //判斷該頂點的是否已經有邊依附
        if (g.node[edge[i][0] - 1].firstarc == NULL) {
            g.node[edge[i][0] - 1].firstarc = next;
        }
        else {//尋找連結串列的最後一個結點
            ArcNode * now;
            now = g.node[edge[i][0] - 1].firstarc;
            while (now->next) {
                now = now->next;
            }
            now->next = next;
        }
    }
}
//列印鄰接表
void print_list(Graph_List g) {
    int i;
    for (i = 0; i < g.vexnum; i++) {
        cout << g.node[i].data << " ";
        ArcNode * now;
        now = g.node[i].firstarc;
        while (now) {
            cout << now->adfvex << " ";
            now = now->next;
        }
        cout << endl;
    }
}

//利用BFS進行圖的遍歷
void BFS_list(Graph_List g, int begin) {
    int i;
    bool * visit = new bool[g.vexnum];
    for (i = 0; i < g.vexnum; i++) {
        visit[i] = false;
    }
    cout << "圖的BFS遍歷結果:" << endl;
    queue<int>  q;
    for (int v = 0; v < g.vexnum; v++) {
        if (!visit[(begin - 1 + v) % g.vexnum])//注意起點不一定是v1
        {
            cout << g.node[(begin - 1 + v) % g.vexnum].data << " ";
            visit[(begin - 1 + v) % g.vexnum] = true;
            q.push((begin - 1 + v) % g.vexnum);//初始化我們的佇列
            while (!q.empty())
            {
                int u = q.front();
                q.pop();
                ArcNode * next;
                next = g.node[u].firstarc;//獲得依附在該頂點的第一條邊的資訊
                while (next) {//遍歷該連結串列上的所有的點
                    if (!visit[next->adfvex]) {
                        cout << g.node[next->adfvex].data << " ";
                        visit[next->adfvex] = true;
                        q.push(next->adfvex);
                    }
                    next = next->next;
                }
            }
        }
    }
}
int main()
{
    Graph_array g;
    Graph_List G;
    int i;
    cout << "輸入圖的種類:" << endl;
    cin >> g.kind; G.kind = g.kind;

    cout << "輸入圖的頂點個數" << endl;
    cin >> g.vexnum; G.vexnum = g.vexnum;

    cout << "輸入圖的邊的個數" << endl;
    cin >> g.edge; G.edge = g.edge;

    g.infromation = new string[g.vexnum];
    G.node = new Vnode[G.vexnum];


    cout << "輸入每個頂點資訊(如名稱):" << endl;
    for (i = 0; i < g.vexnum; i++) {
        cin >> g.infromation[i];
        G.node[i].data = g.infromation[i];
        G.node[i].firstarc = NULL;
    }

    int ** edge_information;
    edge_information = new int*[g.edge];

    cout << "輸入每條邊兩個頂點的編號:" << endl;
    for (i = 0; i < g.edge; i++)
    {
        edge_information[i] = new int[2];
        cin >> edge_information[i][0];
        cin >> edge_information[i][1];
    }

    int **arc; //鄰接矩陣
               //構造鄰接矩陣,其中最後一次引數:1,代表無向圖,0,代表有向圖
    createGraph_by_array(edge_information, g);
    cout << "圖的鄰接矩陣為:" << endl;
    print_array(g);
    cout << endl;
    BFS_array_travel(g, 1);
    cout << endl;

    createGraph_list(G, edge_information);
    cout << "圖的鄰接表為:" << endl;
    print_list(G);
    cout << endl;
    BFS_list(G, 1);
    cout << endl;
    system("pause");
    return 0;


}

同樣是對DFS遍歷的那個圖進行遍歷,結果如下:
這裡寫圖片描述

3、總結

上述我們採用兩種方式對圖進行的了遍歷包括了兩種表達方式:鄰接表和鄰接矩陣,首先我們看遍歷的方法上的選擇,其實這兩種遍歷演算法的時間複雜度是一樣的,它們的不同就是在於遍歷頂點的順序不同,另外,對於兩種圖的不同的表示方式,我們可以發現鄰接矩陣的表示下,圖遍歷的時間複雜度為:o(n*n),而在鄰接表的下,遍歷的時間複雜度只是:O(n+e),其中n為頂點個數,e為邊的條數,所以,在實際中需要進行圖的遍歷的情況下,我們最好是採用鄰接表的方式進行表示我們的圖。