圖的深度優先遍歷和廣度優先遍歷理解

前言

根據分類,圖的搜索分類能夠分爲html

  • BFS和DFS
  • 記憶化搜索(基於深搜)
  • 雙向廣搜
  • 二分狀態搜索
  • 啓發式搜索
  • 與或樹搜索
  • 博弈樹搜索(α-β剪枝)(極大極小過程搜索)
  • A*搜索
  • IDA搜索

先看BFS和DFS,由於這是最基礎的搜索策略了,BFS是按照深度進行搜索,DFS則是按照廣度進行搜索;數組

其實只要你理解了樹的DFS和BFS,那麼圖的話,只是在其基礎上加了判斷結點是否訪問過,是否聯通而已;.net

深度優先搜索

簡介

先看一幅有向圖指針

能夠發現,其遍歷的順序爲code

0->3->1->2->4htm

其的概念就是一條路走到黑blog

圖的表示方法包括鄰接表,鄰接矩陣等,鄰接矩陣表示的是用一個二維數組表示圖的聯通,其中i行j列表示了i結點和j結點的聯通狀況,若是其爲1,說明是聯通的,若是其爲0,反之;鄰接表表示的是一個一維數組,可是數組中每一個列表包含着是鏈表;遞歸

看個圖加深理解:
隊列

其中a是有向圖/原圖,b是鄰接表表示圖,c是鄰接矩陣表示圖;ip

僞代碼

遞歸實現

1. 訪問數組初始化:visited[n] = 0
2. 訪問頂點:visited[v] = 1
3. 取v的第一個鄰接點w;
4. 循環遞歸:
    while(w存在)
        if(w未被訪問過)
            從頂點w出發遞歸執行;
        w = v的下一個鄰接點;

非遞歸實現

1. 棧初始化:visited[n] = 0
2. 訪問頂點:visited[v] = 1
3. 入棧
4. while(棧不爲空)
    x = 棧的頂元素,而且出棧;
    if (存在並找到未被訪問的x的鄰接點w)
        訪問w:visited[w] = 1
        w進棧

上面的尋找下一個鄰接點,須要根據圖是鄰接表仍是鄰接矩陣進行循環判斷;

實現

因爲圖的表示有幾種,因此實現的代碼包括了用鄰接矩陣的圖和鄰接表的圖;

.h文件

typedef struct edge {
    int vertex;
    struct edge* next;
}Edge;

class DFS {
public:
    DFS();
    ~DFS();

    void Test_M();
    void Test_L();
private:
    // 鄰接矩陣
    void createGraph_M(int (*edge)[VERTEXNUM], int start, int end);
    void displayGraph_M(int (*edge)[VERTEXNUM]);
    void DFT_M(int (*edge)[VERTEXNUM], int* vertexStatusArr);
    void DFTCore_M(int (*edge)[VERTEXNUM], int i, int* vertexStatusArr);
    
    // 鄰接表
    void createGraph_L(Edge** edge, int start, int end);
    void displayGraph_L(Edge** edge);
    void delGraph_L(Edge** edge);
    void DFT_L(Edge** edge, int* vertextStatusArr);
    void DFTCore_L(Edge** edge, int i, int* vertexStatusArr);
    
};

void DFSTest_M();
void DFSTest_L();

.cpp文件

DFS::DFS() {
    
}

DFS::~DFS() {
    
    
}

/*----------------------------鄰接矩陣--------------------------*/
/**
 *  建立圖
 *
 *  @param edge <#edge description#>
 */
void DFS::createGraph_M(int (*edge)[VERTEXNUM], int start, int end) {
    edge[start][end] = 1;
}

/**
 *  打印存儲的圖
 *
 *  @param edge <#edge description#>
 */
void DFS::displayGraph_M(int (*edge)[VERTEXNUM]) {
    for (int i = 0; i < VERTEXNUM; i++) {
        for (int j = 0; j < VERTEXNUM; j++) {
            std::cout << edge[i][j];
        }
        std::cout << std::endl;
    }
}

/**
 *  深度優先遍歷
 *
 *  @param edge <#edge description#>
 */
void DFS::DFT_M(int (*edge)[VERTEXNUM], int* vertexStatusArr) {
    for (int i = 0; i < VERTEXNUM; i++) {
        DFTCore_M(edge, i, vertexStatusArr);
    }
    std::cout << std::endl;
}

void DFS::DFTCore_M(int (*edge)[VERTEXNUM], int i, int* vertexStatusArr) {
    if (vertexStatusArr[i] == 1) return; // 頂點訪問過則再也不訪問
    
    std::cout << i << "->";
    vertexStatusArr[i] = 1;
    
    // 存在邊的對應關係,才遞歸
    for (int j = 0; j < VERTEXNUM; j++) {
        if (edge[i][j] == 1) {
            DFTCore_M(edge, j, vertexStatusArr);
        }
    }
}

void DFS::Test_M() {
    std::cout << "--------------鄰接矩陣表示-----------------" << std::endl;
    
    //動態建立存放邊的二維數組
    int (*edge)[VERTEXNUM] = (int (*)[VERTEXNUM])malloc(sizeof(int) * VERTEXNUM * VERTEXNUM);
    
    for (int i = 0; i < VERTEXNUM; i++) {
        for (int j = 0; j < VERTEXNUM; j++) {
            edge[i][j] = 0;
        }
    }
    
    // 存放頂點的遍歷狀態;0:未遍歷,1:已遍歷
    int *vertexStatusArr = (int*)malloc(sizeof(int)*VERTEXNUM*VERTEXNUM);
    for (int i = 0; i < VERTEXNUM; i++) {
        vertexStatusArr[i] = 0;
    }
    
    std::cout << "after init.." << std::endl;
    displayGraph_M(edge);
    
    //建立圖
    createGraph_M(edge, 0, 3);
    createGraph_M(edge, 0, 4);
    createGraph_M(edge, 3, 1);
    createGraph_M(edge, 3, 2);
    createGraph_M(edge, 4, 1);
    
    std::cout << "after create.." << std::endl;
    displayGraph_M(edge);
    
    // 遍歷
    std::cout << "traversal.." << std::endl;
    DFT_M(edge, vertexStatusArr);
    
    free(edge);
}

/*----------------------------鄰接表--------------------------*/
void DFS::createGraph_L(Edge** edge, int start, int end) {
    Edge* nedge = (Edge*)malloc(sizeof(Edge));
    nedge->vertex = end;
    nedge->next = nullptr;
    edge = edge + start;
    while (*edge != nullptr) {
        edge = &((*edge)->next);
    }
    *edge = nedge;
}

void DFS::displayGraph_L(Edge** edge) {
    Edge* nedge;
    int edgeCount = 0;
    for (int i = 0; i < VERTEXNUM; i++) {
        nedge = *(edge + i);
        std::cout << i << ":";
        while (nedge != nullptr) {
            std::cout << nedge->vertex << ",";
            nedge = nedge->next;
            edgeCount++;
        }
        std::cout << std::endl;
    }
    std::cout <<"edge count is " << edgeCount << std::endl;
}

void DFS::delGraph_L(Edge** edge) {
    Edge *p, *del;
    for (int i = 0; i < VERTEXNUM; i++) {
        p = *(edge + i);
        while (p != nullptr) {
            del = p;
            p = p->next;
            free(del);
        }
        edge[i] = nullptr;
    }
    free(edge);
}

void DFS::DFT_L(Edge** edge, int* vertextStatusArr) {
    for (int i = 0; i < VERTEXNUM; i++) {
        DFTCore_L(edge, i, vertextStatusArr);
    }
    std::cout << std::endl;
}

void DFS::DFTCore_L(Edge** edge, int i, int* vertexStatusArr) {
    if (vertexStatusArr[i] == 1) {
        return;
    }
    std::cout << i << "->";
    vertexStatusArr[i] = 1;
    Edge *p = *(edge + i);
    while (p != nullptr) {
        DFTCore_L(edge, p->vertex, vertexStatusArr);
        p = p->next;
    }
}

void DFS::Test_L() {
    std::cout << "--------------鄰接表表示-----------------" << std::endl;
    
    // 動態建立存放邊的指針數組
    Edge **edge = (Edge**)malloc(sizeof(Edge*)*VERTEXNUM);
    for (int i = 0; i < VERTEXNUM; i++) {
        edge[i] = nullptr;
    }
    
    // 存放頂點的遍歷狀態:0:未遍歷,1:已遍歷
    int *vertexStatusArr = (int*)malloc(sizeof(int)*VERTEXNUM);
    for (int i = 0; i < VERTEXNUM; i++) {
        vertexStatusArr[i] = 0;
    }
    
    std::cout << "after init.." << std::endl;
    displayGraph_L(edge);
    
    //建立圖
    createGraph_L(edge, 0, 3);
    createGraph_L(edge, 0, 4);
    createGraph_L(edge, 3, 1);
    createGraph_L(edge, 3, 2);
    createGraph_L(edge, 4, 1);
    
    std::cout << "after create.." << std::endl;
    displayGraph_L(edge);
    
    // 深度優先遍歷
    DFT_L(edge, vertexStatusArr);
    
    // 釋放鄰接表佔用的內存
    delGraph_L(edge);
    edge = nullptr;
    free(vertexStatusArr);
    vertexStatusArr = nullptr;
}

總結:代碼有很強的C的風格,抱歉;這裏面主要看遍歷過程;

廣度優先遍歷

簡介

先看一幅有向圖

能夠發現,其遍歷的順序爲

0->3->4->1->2

其的概念就是左看看右看看,雨露均沾

僞代碼

非遞歸實現

1. 初始化隊列:visited[n] = 0
2. 訪問頂點:visited[v] = 1
3. 頂點v加入隊列
4. 循環:
    while(隊列是否爲空)
        v = 隊列頭元素
        w = v的第一個鄰接點
        while(w存在)
            if(若是w未訪問)
                visited[w] = 1;
                頂點w加入隊列
                w = 頂點v的下一個鄰接點

上面的尋找下一個鄰接點,是尋找以前結點的下一個鄰接點,而不是當前的,不然就變成DFS了;

實現

.h文件

typedef struct bedge {
    int vertex;
    struct bedge* pre;
    struct bedge* next;
}BEdge;

template <class T>
class Queue {
private:
    T *arr;
    int front;
    int rear;
    int size;
    int length;
public:
    Queue();
    ~Queue();
    
    void Push(T value);
    T Pop();
    int Size();
    int Length();
    bool Empty();
    bool Full();
};


class BFS {
private:
    BEdge *front;
    BEdge *rear;
    
public:
    BFS();
    ~BFS();
    
    void Test_M();
    void Test_L();
    
    void createGraph_M(int (*edge)[VERTEXNUM], int start, int end);
    void displayGraph_M(int (*edge)[VERTEXNUM]);
    void BFT_M(int (*edge)[VERTEXNUM], int *vertexStatusArr);
    void BFTCore_M(int (*edge)[VERTEXNUM], int i, int *vertexStatusArr);
    
    void createGraph_L(BEdge** edge, int start, int end);
    void displayGraph_L(BEdge** edge);
    void delGraph_L(BEdge** edge);
    void BFT_L(BEdge** edge, int* vertextStatusArr);
    void BFTCore_L(BEdge** edge, int i, int* vertexStatusArr);
};

void BFSTest_M();
void BFSTest_L();

.cpp文件

template <class T>
Queue<T>::Queue() {
    size = 10;
    arr = new T[size];
    front = rear = length = 0;
}

template <class T>
Queue<T>::~Queue() {
    delete [] arr;
}

template <class T>
void Queue<T>::Push(T value) {
    // 大於原數組長度,建立新數組,賦值給新數組
    if (length == size) {
        int nsize = size * 2;
        int * narr = new T(nsize);
        int i = 0;
        for (; i < length; i++) {
            narr[(front + i) % nsize] = arr[(front + i) % size];
        }
        rear = (front + i) % nsize;
        arr = narr;
        size = nsize;
    }
    arr[rear] = value;
    rear = (rear + 1) % size;
    ++length;
}

template <class T>
T Queue<T>::Pop() {
    T temp = arr[front];
    front = (front + 1) % size; // 原來的內存塊沒有作到真正的刪除
    --length;
    return temp;
}

template <class T>
int Queue<T>::Size() {
    return size;
}

template <class T>
int Queue<T>::Length() {
    return length;
}

template <class T>
bool Queue<T>::Empty() {
    return length == 0;
}

template <class T>
bool Queue<T>::Full() {
    return length == size;
}


BFS::BFS() {
    
    
}

BFS::~BFS() {
    
    
}

void BFS::createGraph_M(int (*edge)[VERTEXNUM], int start, int end) {
    edge[start][end] = 1;
}

void BFS::displayGraph_M(int (*edge)[VERTEXNUM]) {
    for (int i = 0; i < VERTEXNUM; i++) {
        for (int j = 0; j < VERTEXNUM; j++) {
            std::cout << edge[i][j];
        }
        std::cout << std::endl;
    }
}

void BFS::BFT_M(int (*edge)[VERTEXNUM], int *vertexStatusArr) {
    for (int i = 0; i < VERTEXNUM; i++) {
        BFTCore_M(edge, i, vertexStatusArr);
    }
    std::cout << std::endl;
}

void BFS::BFTCore_M(int (*edge)[VERTEXNUM], int i, int *vertexStatusArr) {
    Queue<int> queue;
    queue.Push(i);
    
    while (!queue.Empty()) {
        int t = queue.Pop();
        if (vertexStatusArr[t] == 0) {
            std::cout << t << "->";
            vertexStatusArr[t] = 1;
            for (int i = 0; i < VERTEXNUM; i++) {
                if (edge[t][i] == 1) {
                    queue.Push(i);
                }
            }
        }
    }
}

void BFS::Test_M() {
    std::cout << "--------------鄰接矩陣表示-----------------" << std::endl;
    
    //動態建立存放邊的二維數組
    int (*edge)[VERTEXNUM] = (int (*)[VERTEXNUM])malloc(sizeof(int) * VERTEXNUM * VERTEXNUM);
    
    for (int i = 0; i < VERTEXNUM; i++) {
        for (int j = 0; j < VERTEXNUM; j++) {
            edge[i][j] = 0;
        }
    }
    
    // 存放頂點的遍歷狀態;0:未遍歷,1:已遍歷
    int *vertexStatusArr = (int*)malloc(sizeof(int)*VERTEXNUM*VERTEXNUM);
    for (int i = 0; i < VERTEXNUM; i++) {
        vertexStatusArr[i] = 0;
    }
    
    std::cout << "after init.." << std::endl;
    displayGraph_M(edge);
    
    //建立圖
    createGraph_M(edge, 0, 3);
    createGraph_M(edge, 0, 4);
    createGraph_M(edge, 3, 1);
    createGraph_M(edge, 3, 2);
    createGraph_M(edge, 4, 1);
    
    std::cout << "after create.." << std::endl;
    displayGraph_M(edge);
    
    // 遍歷
    std::cout << "traversal.." << std::endl;
    BFT_M(edge, vertexStatusArr);
    
    free(edge);
    
}

void BFS::createGraph_L(BEdge** edge, int start, int end) {
    BEdge* nedge = (BEdge*)malloc(sizeof(BEdge));
    nedge->vertex = end;
    nedge->next = nullptr;
    edge = edge + start;
    while (*edge != nullptr) {
        edge = &((*edge)->next);
    }
    *edge = nedge;
}

void BFS::displayGraph_L(BEdge** edge) {
    BEdge* nedge;
    int edgeCount = 0;
    for (int i = 0; i < VERTEXNUM; i++) {
        nedge = *(edge + i);
        std::cout << i << ":";
        while (nedge != nullptr) {
            std::cout << nedge->vertex << ",";
            nedge = nedge->next;
            edgeCount++;
        }
        std::cout << std::endl;
    }
    std::cout <<"edge count is " << edgeCount << std::endl;
}

void BFS::delGraph_L(BEdge** edge) {
    BEdge *p, *del;
    for (int i = 0; i < VERTEXNUM; i++) {
        p = *(edge + i);
        while (p != nullptr) {
            del = p;
            p = p->next;
            free(del);
        }
        edge[i] = nullptr;
    }
    free(edge);
}

void BFS::BFT_L(BEdge** edge, int* vertextStatusArr) {
    for (int i = 0; i < VERTEXNUM; i++) {
        BFTCore_L(edge, i, vertextStatusArr);
    }
    std::cout << std::endl;
}

void BFS::BFTCore_L(BEdge** edge, int i, int* vertexStatusArr) {
    Queue<int> queue;
    queue.Push(i);
    
    while (!queue.Empty()) {
        int t = queue.Pop();
        if (vertexStatusArr[t] == 0) {
            std::cout << t << "->";
            vertexStatusArr[t] = 1;
            
            BEdge* p = *(edge + t);
            while (p != nullptr) {
                queue.Push(p->vertex);
                p = p->next;
            }
        }
    }

}

void BFS::Test_L() {
    std::cout << "--------------鄰接表表示-----------------" << std::endl;
    
    // 動態建立存放邊的指針數組
    BEdge **edge = (BEdge**)malloc(sizeof(BEdge*)*VERTEXNUM);
    for (int i = 0; i < VERTEXNUM; i++) {
        edge[i] = nullptr;
    }
    
    // 存放頂點的遍歷狀態:0:未遍歷,1:已遍歷
    int *vertexStatusArr = (int*)malloc(sizeof(int)*VERTEXNUM);
    for (int i = 0; i < VERTEXNUM; i++) {
        vertexStatusArr[i] = 0;
    }
    
    std::cout << "after init.." << std::endl;
    displayGraph_L(edge);
    
    //建立圖
    createGraph_L(edge, 0, 3);
    createGraph_L(edge, 0, 4);
    createGraph_L(edge, 3, 1);
    createGraph_L(edge, 3, 2);
    createGraph_L(edge, 4, 1);
    
    std::cout << "after create.." << std::endl;
    displayGraph_L(edge);
    
    // 深度優先遍歷
    BFT_L(edge, vertexStatusArr);
    
    // 釋放鄰接表佔用的內存
    delGraph_L(edge);
    edge = nullptr;
    free(vertexStatusArr);
    vertexStatusArr = nullptr;
}

參考:

  1. 深度優先遍歷與廣度優先遍歷
  2. 圖之圖的深度優先遍歷
相關文章
相關標籤/搜索