廣度優先(bfs)和深度優先搜索(dfs)的應用實例

廣度優先搜索應用舉例:計算網絡跳數

圖結構在解決許多網絡相關的問題時直到了重要的做用。html

好比,用來肯定在互聯網中從一個結點到另外一個結點(一個網絡到其餘網絡的網關)的最佳路徑。一種建模方法是採用無向圖,其中頂點表示網絡結點,邊表明結點之間的聯接。使用這種模型,能夠採用廣度優先搜索來幫助肯定結點間的最小跳數。node

如圖1所示,該圖表明Internet中的6個網絡結點。以node1做爲起點,有不止1條能夠通往node4的路徑。<node1,node2,node4>,<node1,node3,node2,node4>,<node1,node3,node5,node4>都是可行的路徑。廣度優先搜索能夠肯定最短路徑選擇,即<node1,node2,node4>,一共只須要兩跳。網絡

咱們以bfs做爲廣度優先搜索的函數名(見示例1及示例2)。該函數用來肯定互聯網中兩個結點之間的最小跳數。這個函數有3個參數:graph是一個圖,在這個問題中就表明整個網絡;start表明起始的頂點;hops是返回的跳數鏈表。函數bfs會修改圖graph,所以,若是有必要的話在調用該函數前先對圖建立拷貝。另外,hops中返回的是指向graph中實際頂點的指針,所以調用者必須保證只要訪問hops,graph中的存儲空間就必須保持有效。數據結構

graph中的每個頂點都是一個BfsVertex類型的結構體(見示例1),該結構體有3個成員:data是指向圖中頂點的數據域指針,color在搜索過程當中維護頂點的顏色,hops維護從起始頂點開始到目標頂點的跳數統計。函數

match函數是由調用者在初始化graph時做爲參數傳遞給graph_init的。match函數應該只對BfsVertex結構體中的data成員進行比較。spa

bfs函數將按照前面介紹過的廣度優先搜索的方式來計算。爲了記錄到達每一個頂點的最小跳數,將每一個頂點的hop計數設置爲與該頂點鄰接的頂點的hop計數加1。對於每一個發現的頂點都這樣處理,並將其塗成灰色。每一個頂點的顏色和跳數信息都由鄰接表結構鏈表中的BfsVertex來維護。最後,加載hops中全部跳數未被標記爲-1的頂點。這些就是從起始頂點可達的頂點。指針

bfs的時間複雜度爲O(V+E),這裏V表明圖中的頂點個數,E是邊的個數。這是由於初始化頂點的顏色屬性以及確保起始頂點存在都須要O(V)的運行時間,廣度優先搜索中的循環的複雜度是O(V+E),加載跳數統計鏈表的時間爲O(V)。code

示例1:廣度優先搜索的頭文件htm

/*bfs.h*/
#ifndef BFS_H
#define BFS_H

#include "graph.h"
#include "list.h"

/*定義廣度優先搜索中的頂點數據結構*/
typedef struct BfsVertex_
{
    void *data;
    VertexColor color;
    int hops;
}BfsVertex;

/*函數接口定義*/
int bfs(Graph *graph, BfsVertex *start, List *hops);

#endif // BFS_H

 示例2:廣度優先搜索的實現blog

/*bfs.c*/
#include <stdlib.h>
#include "bfs.h"
#include "graph.h"
#include "list.h"
#include "queue.h"

/*bfs */
int bfs(Graph *graph, BfsVertex *start, List *hops)
{
    Queue     queue;
    AdjList   *adjlist, *clr_adjlist;
    BfsVertex *clr_vertex, *adj_vertex;
    ListElmt  *element, *member;
    
    /*初始化圖中的全部結點爲廣度優先結點*/
    for(element = list_head(&graph_adjlists(graph)); element != NULL; element = list_next(element))
    {
        clr_vertex = ((AdjList *)list_data(element))->vertex;
        if(graph->match(clr_vertex,start))
        {
            /*初始化起始頂點*/
            clr_vertex->color = gray;
            clr_vertex->hops  = 0;
        }
        else
        {
            /*初始化非起始頂點*/
            clr_vertex->color = white;
            clr_vertex->hops = -1;
        }
    }
    /*初始化隊列,並將起始頂點的鄰接表入隊*/
    queue_init(&queue,NULL);
        /*返回起始頂點的鄰接表,存儲到clr_adjlist*/
    if(graph_adjlist(graph,start,&clr_adjlist) != 0)
    {
        queue_destroy(&queue);
        return -1;
    }
        /*將頂點的鄰接表入隊到隊列*/
    if(queue_enqueue(&queue,clr_adjlist) != 0 )
    {
        queue_destroy(&queue);
        return -1;
    }
    
    /*執行廣度優先探索*/
    while(queue_size(&queue) > 0)
    {
        adjlist = queue_peek(&queue);
        
        /*遍歷鄰接表中的每個頂點*/
        for(member = list_head(&adjlist->adjacent); member != NULL; member = list_next(member))
        {
            adj_vertex = list_data(member);
            
            /*決定下一個鄰接點的顏色*/
            if(graph_adjlist(graph,adj_vertex,&clr_adjlist) != 0)
            {
                queue_destroy(&queue);
                return -1;
            }
            clr_vertex = clr_adjlist->vertex;
            
            /*把白色的頂點標成灰色,並把它的鄰接頂點入隊*/
            if(clr_vertex->color == white)
            {
                clr_vertex->color = gray;
                clr_vertex->hops = ((BfsVertex *)adjlist->vertex)->hops + 1;
                
                if(queue_enqueue(&queue,clr_adjlist) != 0)
                {
                    queue_destroy(&queue);
                    return -1;
                }
            }
        }
        
        /*將當前頂點鄰接表從隊列中移除並塗成黑色*/
        if(queue_dequeue(&queue,(void **)&adjlist) == 0)
        {
            ((BfsVertex *)adjlist->vertex)->color = black;
        }
        else
        {
            queue_destroy(&queue);
            return -1;
        }
    }
    
    queue_destroy(&queue);
    
    /*返回每個頂點的hop計數到一個鏈表中*/
    list_init(hops,NULL);
    
    for(element = list_head(&graph_adjlists(graph)); element != NULL; element = list_next(element))
    {
        /*跳過那些沒有被訪問到的節點(hops = -1)*/
        clr_vertex = ((AdjList *)list_data(element))->vertex;
        if(clr_vertex->color != -1)
        {
            if(list_ins_next(hops,list_tail(hops),clr_vertex) != 0)
            {
                list_destroy(hops);
                return -1;
            }
        }
    }
    return 0;
}

 深度優先搜索的應用舉例:拓撲排序

有時候,咱們必須根據各類事物間的依賴關係來肯定一種可接受的執行順序。好比,在大學裏必須知足一些先決條件才能選的課程,或者一個複雜的項目,其中某個特定的階段必須在其餘階段開始以前完成。要爲這一類問題建模,能夠採用優先級圖,其採用的是有向圖的思路在優先級圖中,頂點表明任務,而邊表明任務之間的依賴關係以必須先完成的任務爲起點,以依賴於此任務的其餘任務爲終點,畫一條邊便可。

以下圖所示,它表示7門課程及其先決條件組成的一份課程表:S100沒有先決條件,S200須要S100,S300須要S200和M100,M100沒有先決條件,M200須要M100,M300須要S300和M200,而且S150沒有先決條件同時也不是先決條件。

經過對這些課程執行拓撲排序,深度優先搜索有助於肯定出一中可接受的順序。

拓撲排序將頂點排列爲有向無環圖,所以全部的邊都是從左到右的方向。正規來講,有向無環圖G=(V,E)的拓撲排序是其頂點的一個線性排序,以便若是G中存在一條邊(u,v),那麼線性順序中u出如今v的前面,在許多狀況下,知足此條件的順序有多個。

下面的代碼示例實現了函數dfs,即深度優先搜索。該函數在這裏用來對任務作拓撲排序。dfs有兩個參數:graph表明圖,在這個問題中則表明須要排序的任務;而參數ordered是完成拓撲排序後返回的頂點鏈表。調用該函數會修改圖graph,所以若是有必要須要在調用前先對graph建立一個副本。另外,函數返回後鏈表ordered中保存了指向圖graph中頂點的指針,所以調用者必須保證,一旦訪問ordered中的元素就必須保證graph中的存儲空間保持有效。graph中的每個頂點都是一個DfsVertex結構體,該結構體擁有兩個成員:data是指向頂點數據域部分的指針;而color在搜索過程當中負責維護頂點的顏色信息。match函數是由調用者在初始化graph時經過參數傳遞給graph_init的,該函數應該只對DfsVertex結構體中的data成員進行比較。

dfs按照深度優先的方式進行搜索。dfs_main是實際執行搜索的函數。dfs中的最後一個循環保證對圖中全部未相連的元素完成了檢索。在dfs_main中逐個完成頂點的搜索並將其塗黑,而後插入鏈表ordered的頭部。最後,ordered就包含完整拓撲排序後的頂點。

dfs的時間複雜度是O(V+E),V表明圖中的頂點個數,而E表明邊的個數。這是由於初始化頂點的顏色信息須要O(V)的時間,而dfs_main的時間複雜度爲O(V+E)。

示例3:深度優先搜索的頭文件

/*dfs.h*/
#ifndef DFS_H
#define DFS_H

#include "graph.h"
#include "list.h"

/*爲深度優先搜索中的全部節點定義一個結構體*/
typedef struct DfsVertex_
{
    void *data;
    VertexColor color;
}DfsVertex;

/*公共接口*/
int dfs(Graph *graph,List *ordered);

#endif // DFS_H

 

 示例4:深度優先搜索的函數實現

/*dfs.c*/
#include <stdlib.h>

#include "dfs.h"
#include "graph.h"
#include "list.h"

/*dfs_main*/
static int dfs_main(Graph *graph, AdjList *adjlist, List *ordered)
{
    AdjList   *clr_adjlist;
    DfsVertex *clr_vertex, *adj_vertex;
    ListElmt  *member;
    
    /*首先,將起始頂點塗成灰色,並遍歷它的鄰接頂點集合*/
    ((DfsVertex *)adjlist->vertex)->color = gray;
    
    for(member = list_head(&adjlist->adjacent); member != NULL; member = list_next(member))
    {
        /*決定下一個集合頂點的顏色*/
        adj_vertex = list_data(member);
        
        if(graph_adjlist(graph,adj_vertex,&clr_adjlist) != 0)
        {
            return -1;
        }
        
        clr_vertex = clr_adjlist->vertex;
        /*若是當前頂點是白色,則遞歸搜索它的鄰接點*/
        if(clr_vertex->color == white)
        {
            if(dfs_main(graph,clr_adjlist,ordered) != 0)
                return -1;
        }
    }
    
    /*把當前頂點塗成「黑」色,並加入到鏈表頭部*/
    ((DfsVertex *)adjlist->vertex)->color = black;
    
    if(list_ins_next(ordered, NULL, (DfsVertex *)adjlist->vertex) !=0 )
        return -1;
    
    return 0;
}

/*dfs*/
int dfs(Graph *graph, List *ordered)
{
    DfsVertex *vertex;
    ListElmt  *element;
    
    /*初始化圖中的全部頂點*/
    for(element = list_head(&graph_adjlists(graph)); element != NULL; element = list_next(element))
    {
        vertex = ((AdjList *)list_data(element))->vertex;
        vertex->color = white;
    }
    
    /*執行廣度優先搜索*/
    list_init(ordered,NULL);
    
    for(element = list_head(&graph_adjlists(graph)); element != NULL; element = list_next(element))
    {
        /*確保圖中的每一個頂點都能被檢索到*/
        vertex = ((AdjList *)list_data(element))->vertex;
        
        if(vertex->color == white)
        {
            if(dfs_main(graph, (AdjList *)list_data(element), ordered) != 0)
            {
                list_destroy(ordered);
                return -1;
            }
        }
    }
    
    return 0;
}
相關文章
相關標籤/搜索