圖是一種靈活的數據結構,它多用於描述對象之間的關係和鏈接模型。html
關於圖的算法:最小生成樹、最短路徑、旅行商問題以及許多其餘算法大都會使用到廣度優先搜索和深度優先搜索,由於它們提供了一套系統地訪問圖數據結構的方法。算法
帶權圖,是指圖的每條邊上帶有一個值或權,這些權用一個小的數字標記在邊上。不少條件因素均可以做爲權值,但一般它表示遍歷這條邊所產生的代價。數據結構
咱們作一個簡單的模型,在一塊木板上釘上一些釘子並用一些細繩鏈接起來,假設每個釘子都經由一根或多根細繩鏈接起來。如今咱們一根一根拿走細繩,直到用最少的細繩將全部釘子鏈接起來。這個模型的背後思想就是最小生成樹。函數
正式的表述法是,給定一個無方向的帶權圖G=(V,E),最小生成樹爲集合T,T是以最小代價鏈接V中全部頂點所用邊E的最小集合。集合T中的邊能造成一顆樹,這是由於每一個頂點都能向上找到它的一個父節點(根結點除外)。spa
Prim算法是一種產生最小生成樹的方法。3d
Prim算法從任意一個頂點開始,每次選擇一個與當前頂點最近的一個頂點,並將兩個頂點之間的邊加入到樹中。code
從根本上講,Prim算法就是不斷的選擇頂點,並計算邊的權值,同時判斷是否還有更有效的鏈接方式。該算法相似廣度優先搜索算法,由於在往圖中更深的頂點探索以前,它首先要遍歷與此頂點相關的全部頂點。每一個階段都要決定選擇哪一個頂點,因此須要維護頂點的顏色和鍵值。htm
開始,將全部頂點的色值設置爲白色,鍵值設置爲∞(它表明一個足夠大的數,大於圖中全部邊的權值)。同時,將起始頂點的鍵值設置爲0。隨着算法的不斷演進,在最小生成樹中爲每一個頂點(除起始頂點外)指派一個父節點。只有當頂點的色值變爲黑色時,此頂點纔是最小生成樹的一部分。對象
Prim算法的運行過程以下:blog
首先,在圖中全部的白色頂點中,選擇鍵值最小的頂點u。開始,鍵值被設置爲0的那一頂點將做爲起始頂點。
當選擇此頂點以後,將其標記爲黑色。
接下來,對於每一個與u相鄰的頂點v,設置v的鍵值爲邊(u,v)的權值,同時將u設置爲v的父結點。
重複這個過程,直到全部的頂點都標記爲黑色。
隨着最小生成樹的增加,該樹會包含圖中的全部的邊(鏈接全部頂點最少數量邊),且每條邊的兩端都有一個黑色的頂點。
下圖展現了最小生成樹的產生過程。在圖中,鍵值和父結點都顯示在每一個頂點的旁邊,用斜線分開。鍵值顯示在斜線的左邊,父結點顯示在斜線的右邊。淺灰色的邊是最小生成樹增加過程當中的邊。圖中最小生成樹總的權值爲17。
mst
int mst(Graph *graph, const MstVertex *start, List *span, int (*match)(const void *key1, const void *key2));
返回值:若是計算最小生成樹成功,返回0,不然返回-1。
描述:爲一個無方向的帶權圖graph計算最小生成樹。
最小生成樹從頂點start開始。
此操做會改變graph,因此若是有必要,在調用此操做以前先對圖進行備份。
graph中的每一個頂點必須包含MstVertex類型的數據。經過設置MstVertex結構體中的成員weight的值來指定每一個邊的權值,weight的值由傳入graph_ins_edge的參數data2決定。用MstVertex結構體的成員data來保存與頂點相關的數據。
graph的match函數(此函數在用graph_init對圖進行初始化時調用)用來比較MstVertex結構體中的data成員。此函數與傳入mst中的參數match相同。
一旦計算完成,最小生成樹的相關數據將會返回到span。span是存儲MstVertex結構體的列表。在span中,父結點爲NULL的頂點爲最小生成樹的根結點。其餘每一個頂點的parent成員都指向span中位於該頂點以前的那個頂點。
span中的頂點指向graph中的實際頂點,因此只要可以訪問span,函數調用者就必須保證graph中內存空間有效。一旦再也不使用span,就調用list_destroy銷燬span。
複雜度:O(E V2),其中V是圖中頂點的個數,E是邊的條數。
爲了計算一個無方向的帶權圖的最小生成樹,首先,咱們要用表示圖的基本抽象數據類型來表示帶權圖。同時,Prim算法還須要一種追蹤頂點和邊信息的方法。這就用到了MstVertex結構體:它用來爲圖中的頂點計算最小生成樹。此結構包含5個成員:data是與頂點相關的數據;weight是到達該頂點的邊的權值;color是頂點的顏色;key是頂點的鍵值;parent是最小生成樹中頂點的父結點。
創建一個包含MstVertex結構體的圖的過程幾乎與創建一個包含其餘類型的圖的過程同樣:要將一個頂點插入圖中,調用graph_ins_vertex,並將MstVertex結構體傳入data。相似地,要將一條邊插入圖中,調用函數graph_ins_edge,並將MstVertex結構體傳入data1和data2。當插入一個頂點時,只設置MstVertex結構體的data成員。當插入一條邊時,設置data1的data成員,data2的data和weight成員。在data2中,weight是邊的權值,此邊是data1中的頂點到data2中頂點的鏈接線。在實際中,權值一般用浮點數進行存儲和計算。因爲鍵值是由權值計算來的,所以鍵值也用浮點數表示。
mst操做首先初始化鄰接表結構鏈表中的每一個頂點。將每一個頂點的鍵值初始化爲DBL_MAX(除超始頂點外,超始頂點初始值爲0.0)。在圖的抽象數據類型中,圖由一個鄰接表結構鏈表來表示。每一個鄰接表包含一個頂點和一個相鄰頂點的集合。用存儲在鄰接表結構中的頂點來維護頂點的色值、鍵值、和父結點。維護鄰接表結構鏈表中信息的關鍵是能將這些信息存儲起來,而不是僅僅列出與本身相鄰的頂點。鑑於一個頂點可能會出如今衆多的鄰接表中,因此每一個頂點只能在鄰接表結構鏈表中出現一次。
Prim算法的核心是用一個單循環爲圖中的每一個結點迭代一次。在每次迭代的過程當中:
首先,在全部的白色頂點中選擇鍵值最小的頂點。同時,在鄰接表結構鏈表把此頂點塗黑。
接下來,遍歷與所選頂點相鄰的頂點。在遍歷每一個頂點時,檢查它在鄰接表結構鏈表中的顏色和鍵值。一旦獲取了這些信息,就將它與所選頂點的顏色和鍵值進行比較。若是相鄰頂點是白色,且其鍵值比所選頂點的小,就將所選頂點與相鄰頂點之間邊的權值設置爲相鄰頂點的鍵值;同時,將相鄰頂點的父結點設置爲所選頂點。
而後,更新存儲在鄰接表結構鏈表中相鄰頂點的信息。
重複這個過程,直到全部頂點都塗黑。
一旦Prim算法中的主循環結束,最小生成樹也就計算完成了。此時,將鄰接表結構鏈表中的每一個黑色MstVertex結構體插入到鏈表span中。在span中,父結點爲NULL的頂點就是最小生成樹的根結點。其餘每一個頂點的parent成員都指向span中位於該頂點以前的那個頂點。每一個MstVertex結構體的成員weight並不常用,由於它只有在存儲到鄰接表中時才用的到。
下圖顯示了上面的示例圖中計算最小生成樹所返回的MstVertex結構體鏈表:
示例:圖算法的頭文件(含最小生成樹、最短路徑、旅行商問題三種實現所需函數定義的頭文件)
/*graphalg.h*/ #ifndef GRAPHALG_H #define GRAPHALG_H #include "graph.h" #include "list.h" /*定義最小生成樹中結點的數據結構*/ typedef struct MstVertex_ { void *data; double weight; VertexColor color; double kdy; struct MstVertex_ *parent; }MstVertex; /*定義最短路徑中結點的數據結構*/ typedef struct PathVertex_ { void *data; double weight; VertexColor color; double d; struct PathVertex_ *parent; }PathVertex; /*定義旅行商問題中結點的數據結構*/ typedef struct TspVertex_ { void *data; double x,y; VertexColor color; }TspVertex; /*函數接口*/ int mst(Graph *graph, const MstVertex *start, List *span, int (*match)(const void *key1, const void *key2)); int shortest(Graph *graph, const PathVertex *start, List *paths, int(*match)(const void *key1, const void *key2)); int tsp(List *vertexs, const TspVertex *start, List *tour, int (match*)(const void *key1, const void *key2)); #endif // GRAPHALG_H
示例:計算最小生成樹的實現
/*mst.c*/ #include <float.h> #include <stdlib.h> #include "graph.h" #include "list.h" /*mst 計算最小生成樹函數*/ int mst(Graph *graph, const MstVertex *start, List *span, int (*match)(const void *key1, const void *key2)) { AdjList *adjlist; MstVertex *mst_vertex, *adj_vertex; ListElmt *element, *member; double minmum; int found,i; /*初始化圖中的全部結點*/ found=0; for(element=list_head(&graph_adjlists(graph)); element!=NULL; element = list_next(element)) { mst_vertex = ((AdjList *)list_data(element))->vertex; if(match(mst_vertex,start)) { /*匹配到起始頂點,並對其初始化*/ mst_vertex->color = white; mst_vertex->key = 0; mst_vertex->parent = NULL; found = 1; } else { /*非起始頂點的初始化*/ mst_vertex->color = white; mst_vertex->key = DBL_MAX; mst_vertex->parent = NULL; } } /*未找到起始頂點,函數返回*/ if(!found ) return -1; /*運用Prim算法計算最小生成樹*/ i=0; while(i<graph_vcount(graph)) { /*選擇擁有最小鍵值的白色頂點*/ minimum = DBL_MAX; for(element = list_head(&graph_adjlists(graph)); element != NULL; element = list_next(element)) { mst_vertex = ((AdjList *)list_data(element))->vertex; if(mst_vertex->color == white && mst_vertex->key < minmum) { minmum = mst_vertex->key; adjlist = list_data(element); } } /*將已選擇的頂點塗成黑色*/ ((MstVertex *)adjlist->vertex)->color = black; /*遍歷被選中頂點的全部鄰接頂點*/ for(member = list_head(&adjlist->adjacent); member != NULL; member = list_next(member)) { adj_vertex = list_data(member); for(element = list_head(&graph_adjlists(graph)); element != NULL; elemet = list_next(element)) { mst_vertex = ((AdjList *)list_data(element))->vertex; if(match(mst_vertex,adj_vertex)) { /*決定是否改變該頂點的鍵值或父結點*/ if(mst_vertex->color == white && adj_vertex->weight < mst_vertex->key) { mst_vertex->key = adj_vertex->weight; mst_vertex->parent = adjlist_vertex; } break; } } } /*準備選取下一個頂點*/ i++; } /*加載最小生成樹到鏈表中*/ list_init(span,NULL); /*從鄰接表結構鏈表中加載每一個黑色結點*/ for(element = list_head(&graph_adjlists(graph)); elemet != NULL; element = list_next(element)) { /**/ mst_vertex = ((AdjList *)list_data(element))->vertex; if(mst_vertex->color == black) { if(list_ins_next(span, list_tail(span),mst_vertex) != 0) { list_destroy(span); return -1; } } } return 0; }