圖算法--最小生成樹算法的實現與分析

2021-03-02 Unity3D遊戲開發精華教程乾貨

圖是一種靈活的數據結構,它多用於描述對象之間的關係和連接模型。

關於圖的算法:最小生成樹、最短路徑、旅行商問題以及許多其他算法大都會使用到廣度優先搜索和深度優先搜索,因為它們提供了一套系統地訪問圖數據結構的方法。

帶權圖,是指圖的每條邊上帶有一個值或權,這些權用一個小的數字標記在邊上。很多條件因素都可以作為權值,但通常它表示遍歷這條邊所產生的代價。最小生成樹簡述

我們做一個簡單的模型,在一塊木板上釘上一些釘子並用一些細繩連接起來,假設每一個釘子都經由一根或多根細繩連接起來。現在我們一根一根拿走細繩,直到用最少的細繩將所有釘子連接起來。這個模型的背後思想就是最小生成樹。

正式的表述法是,給定一個無方向的帶權圖G=(V,E),最小生成樹為集合T,T是以最小代價連接V中所有頂點所用邊E的最小集合。集合T中的邊能形成一顆樹,這是因為每個頂點都能向上找到它的一個父節點(根結點除外)。Prim算法Prim算法是一種產生最小生成樹的方法。Prim算法從任意一個頂點開始,每次選擇一個與當前頂點最近的一個頂點,並將兩個頂點之間的邊加入到樹中。

從根本上講,Prim算法就是不斷的選擇頂點,並計算邊的權值,同時判斷是否還有更有效的連接方式。該算法類似廣度優先搜索算法,因為在往圖中更深的頂點探索之前,它首先要遍歷與此頂點相關的所有頂點。 每個階段都要決定選擇哪個頂點,所以 需要維護頂點的顏色和鍵值 。

開始,將所有頂點的色值設置為白色,鍵值設置為∞(它代表一個足夠大的數,大於圖中所有邊的權值)。同時,將起始頂點的鍵值設置為0。隨著算法的不斷演進,在最小生成樹中為每個頂點(除起始頂點外)指派一個父節點。只有當頂點的色值變為黑色時,此頂點才是最小生成樹的一部分。

Prim算法的運行過程如下:

首先,在圖中所有的白色頂點中,選擇鍵值最小的頂點u。開始,鍵值被設置為0的那一頂點將作為起始頂點。

當選擇此頂點之後,將其標記為黑色。

接下來,對於每個與u相鄰的頂點v,設置v的鍵值為邊(u,v)的權值,同時將u設置為v的父結點。

重複這個過程,直到所有的頂點都標記為黑色。

隨著最小生成樹的增長,該樹會包含圖中的所有的邊(連接所有頂點最少數量邊),且每條邊的兩端都有一個黑色的頂點。

下圖展示了最小生成樹的產生過程。在圖中,鍵值和父結點都顯示在每個頂點的旁邊,用斜線分開。鍵值顯示在斜線的左邊,父結點顯示在斜線的右邊。淺灰色的邊是最小生成樹增長過程中的邊。圖中最小生成樹總的權值為17。

最小生成樹的接口定義mstint 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 V 2 ),其中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結構體鍊表:

示例:圖算法的頭文件(含最小生成樹、最短路徑、旅行商問題三種實現所需函數定義的頭文件)
#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

示例:計算最小生成樹的實現

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;
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;}


聲明:發布此文是出於傳遞更多知識以供交流學習之目的。若有來源標註錯誤或侵犯了您的合法權益,請作者持權屬證明與我們聯繫,我們將及時更正、刪除,謝謝。

作者:DreamGo

來源:http://www.cnblogs.com/idreamo/p/9472295.html

More:【微信公眾號】 u3dnotes

相關焦點

  • 最小生成樹的兩種方法:Kruskal 算法和 Prim 算法
    強連通圖:在有向圖中,若任意兩個頂點vivi與vjvj都有路徑相通,則稱該有向圖為強連通圖。連通網:在連通圖中,若圖的邊具有一定的意義,每一條邊都對應著一個數,稱為權;權代表著連接連個頂點的代價,稱這種連通圖叫做連通網。生成樹:一個連通圖的生成樹是指一個連通子圖,它含有圖中全部n個頂點,但只有足以構成一棵樹的n-1條邊。
  • 李曉明教授作業題算法的python編程實現
    >   11月10日,北京大學李曉明教授在浙江省江山中學上了一堂精彩的高中信息技術新課標的試驗課,其課堂鞏固環節的作業題如下:  比較普通的算法是用普裡姆算法實現
  • 基於矩陣的故障樹分析方法
    而後,FTA得到了極大的發展,如今已經廣泛應用於航天工業、機械製造、核電站、海洋工程等領域的系統可靠性分析中。 在進行故障樹分析時,對故障樹的建模和計算是非常繁瑣費時的,當分析對象趨於大型化、複雜化,必須藉助故障樹分析軟體來實現。
  • K近鄰算法(KNN)原理小結
    KNN算法之暴力實現原理4. KNN算法之KD樹實現原理5. KNN算法之訓練樣本不平衡情況6. 算法優缺點KNN算法是選擇與輸入樣本在特徵空間內最近鄰的k個訓練樣本並根據一定的決策規則,給出輸出結果 。決策規則:分類任務:輸出結果為k個訓練樣本中佔大多數的類 。
  • 教程 從頭開始:用Python實現隨機森林算法
    除了仍然根據從訓練數據樣本建立複合模型之外,隨機森林對用做構建樹(tree)的數據特徵做了一定限制,使得生成的決策樹之間沒有關聯,從而提升算法效果。本教程旨在探討如何用 Python 實現隨機森林算法。
  • 生成樹協議詳解
    由於生成樹協議本身比較小,所以並不像路由協議那樣廣為人知。但是它卻掌管著埠的轉發大權—「小樹枝抖一抖,上層協議就得另謀生路」。真實情況也確實如此,特別是在和別的協議一起運行的時候,生成樹就有可能斷了其他協議的報文通路,造成種種奇怪的現象。生成樹協議和其他協議一樣,是隨著網絡的不斷發展而不斷更新換代的。
  • 數據結構與算法——圖最短路徑
    圖的最短路徑:如果從有向圖中某一頂點(稱為源點)到達另一頂點(稱為終點)的路徑可能不止一條,如何找到一條路徑使得沿此路徑上各邊上的權值總和達到最小。3 深度或廣度優先搜索算法3.1 算法概述   從起點開始訪問所有深度遍歷路徑或廣度優先路徑,則到達終點節點的路徑有多條,取其中路徑權值最短的一條則為最短路徑。
  • LightGBM最強解析,從算法原理到代碼實現~
    1.2 XGBoost的缺點及LightGBM的優化(1)XGBoost的缺點在LightGBM提出之前,最有名的GBDT工具就是XGBoost了,它是基於預排序方法的決策樹算法。這種構建決策樹的算法基本思想是:首先,對所有特徵都按照特徵的數值進行預排序。
  • 主流Gradient Boosting算法對比
    在介紹GBM之前,我們先回顧一下數值優化中的梯度下降算法:設其中P表示參數、x表示樣本、表示使最小的值,但這個式子往往無法求解,所以數值優化的思想就是用多次迭代去逼近最優解,令這裡的,我們只需在每個迭代中求出pi即可。
  • 用Python實現神奇切圖算法Seam Carving
    算法移除了一些巖石以及湖水(所以我們看到圖中的小船離得更近了)。這就是接縫剪裁算法的神奇之處,它能在調整圖像大小本身的同時,也能保留圖像中最重要最突出的內容。如果我們在切圖時,既想獲得合適的圖像大小,也想保留圖像的完整內容,使用傳統的切圖方法幾乎無法做到。而使用接縫剪裁算法就能實現二者兼得。關於算法的核心原理,在原論文中解釋的非常清楚了,網上也有很多解析文章,這裡不再贅述。
  • 來Offer技術講解 | 最近鄰搜索算法
    為了減少探測數量,論文裡他們限制了每個LSH的探測區間,並且對一個哈希桶內所有的LSH進行了估計,設定了探測順序,使得人們有更大的可能性儘早探測到最近鄰。對於有M個LSH構成的哈希桶,最近有2M個探測方向,在選擇的時候可以用一個優先隊列,比如MinHeap來維護探測哈希桶的選擇,採用shift和expand兩種操作生成所有探測可能性。
  • 比較全面的Adaboost算法總結(一)
    算法串行生成多個弱學習器並按一定的結合策略生成強學習器,AdaBoost算法是Boosting系列算法中的一種,本文詳細總結了AdaBoost算法的相關理論。Boosting算法基本原理2. Boosting算法的權重理解3. AdaBoost的算法流程4. AdaBoost算法的訓練誤差分析5. AdaBoost算法的解釋6. AdaBoost算法的正則化7. AdaBoost算法的過擬合問題討論8.
  • 綜述推薦 | 面向無人機集群路徑規劃的智能優化算法綜述
    智能優化算法因其對優化問題的性質要求低、魯棒性高,而被廣泛應用於求解路徑規劃問題.鑑於此, 本文綜述了近些年面向無人機集群路徑規劃的智能優化算法研究, 首先介紹了無人機集群路徑規劃的基本模型, 包括規劃空間表示、優化目標函數及約束條件等, 其次闡述了基於不同智能優化算法的無人機集群路徑規劃研究現狀、詳細對比分析了不同類型算法的優勢與不足, 最後對無人機集群路徑規劃研究進行了展望.
  • 算法筆記:圖論中的單源最短路徑算法——Bellman-Ford 算法
    Dijkstra 算法是不能用於有負邊的圖,但實際上,有負權值邊的圖是可能存在最短路徑的。
  • 基於深度學習的目標檢測算法研究進展
    自1998年美國工程師提出目標檢測概念以來,產生了大量基於手工設計特徵的傳統算法。這些算法大多是借鑑窮舉的思想,在基於滑動窗口生成的候選框內提取特徵,並將特徵交給分類器去識別。常見方法包括Hear特徵+Adaboost算法、Hog特徵+SVM算法等。因為早期很多的目標檢測算法缺乏有效的特徵表示,所以設計了許多複雜的特徵表示和在有限資源情況下處理特徵加速的技巧。
  • ISP基本框架及算法介紹
    目前最常用的插補算法是利用該像素點周圍像素的平均值來計算該點的插補值。如下圖所示,左側是RAW域原始圖像,右側是經過插值之後的圖像。      RGB轉YUV可以參考:色彩轉換系列之RGB格式與YUV格式互轉原理及實現12.WDR(Wide Dynamic Range)-寬動態    動態範圍(Dynamic Range)是指攝像機支持的最大輸出信號和最小輸出信號的比值,或者說圖像最亮部分與最暗部分的灰度比值。
  • 如何用純CSS實現輪播圖效果
    1、結構搭建:首先要有一個容器作為輪播圖的容器,同時要實現圖片切換,所以內部要有一個裝所有待切換內容的子容器。由於子容器中的內容是左右切換的,所以要將內容左右排列開。Html代碼如下:2、 CSS實現靜態效果:
  • 機器學習算法一覽(附python和R代碼)
    常見的機器學習算法以下是最常用的機器學習算法,大部分數據問題都可以通過它們解決:1.線性回歸 (Linear Regression)2.邏輯回歸 (Logistic Regression)3.決策樹 (Decision Tree)4.支持向量機(SVM)5.樸素貝葉斯 (Naive Bayes)6.K鄰近算法(KNN)7.K-均值算法(K-means)8
  • LightGBM算法總結
    LightGBM (Light Gradient Boosting Machine)是一個實現 GBDT 算法的框架,支持高效率的並行訓練,並且具有以下優點:    更快的訓練速度    更低的內存消耗    更好的準確率    分布式支持,可以快速處理海量數據 如下圖,在 Higgs 數據集上 LightGBM 比 XGBoost
  • 教程 | 聽說你了解深度學習最常用的學習算法:Adam優化算法?
    Adam 最開始是由 OpenAI 的 Diederik Kingma 和多倫多大學的 Jimmy Ba 在提交到 2015 年 ICLR 論文(Adam: A Method for Stochastic Optimization)中提出的。本文前後兩部分都基於該論文的論述和解釋。首先該算法名為「Adam」,其並不是首字母縮寫,也不是人名。