一、基礎概念
Prim演算法是一種用於求解最小生成樹的演算法。所謂最小生成樹,就是一顆包含圖上所有節點,且邊權值之和最小的連通子圖。
對於一張無向圖G = (V, E),其中V為節點集合,E為邊集合,任意兩個節點之間存在一條邊。Prim演算法運行於這樣的圖上,它以任意一個節點為起點開始,不斷從當前連通子圖中尋找且斜率最小的邊,加入新的節點,最終生成最小生成樹。
二、Prim演算法原理
1. 數據結構
為了方便Prim演算法的實現,我們需要準備兩個數據結構——
1)dist:記錄每個節點與當前最小生成樹的最小邊權值。初始化為無窮大(除起點外)
2)used:記錄每個節點是否已經納入最小生成樹的集合當中,初始化為false。
2. 尋找最小邊
在無向連通圖中,每個節點有多個可選的連邊。對於Prim演算法的核心思路,我們可以通過找到當前連通子圖能夠到達的節點中,邊權值最小的那一條邊,來實現新節點加入最小生成樹。
// 對於每個節點i,記錄它和最小生成樹聯繫最近的邊 int min_edge[N]; void prim(int st){ // 將st標記為已經訪問的節點 used[st] = true; // 初始化dist數組 for(int i = 0; i < n; i++){ dist[i] = i == st ? 0 : INF; min_edge[i] = st; } while(true){ int v = -1; // 在未訪問過的節點中選擇最小的dist保存在v中 for(int u = 0; u < n; u++){ if(!used[u] && (v == -1 || dist[u] < dist[v])){ v = u; } } if(v == -1) break; used[v] = true; // 更新最小生成樹,利用min_edge數組記錄 for(int u = 0; u cost[v][u]){ dist[u] = cost[v][u]; min_edge[u] = v; } } } }
3. Prim演算法實現
結合上述兩步內容,我們就可以實現完整的Prim演算法。需要注意的一些問題包括:
1)Prim演算法非常依賴於數據結構,因此開發者需要清楚地了解數據結構的特點、優缺點,併合理運用。
2)Prim演算法的時間複雜度為O(n2),因此在處理複雜的大數據集時,需要注意優化演算法實現。
// Prim演算法的完整實現 const int MAXN = 1010; int cost[MAXN][MAXN]; // 圖上兩節點之間的邊權值 int dist[MAXN]; bool used[MAXN]; int n; void prim(){ for(int i = 0; i < n; i++){ dist[i] = INF; used[i] = false; } dist[0] = 0; //起始點從0開始 int res = 0; while(true){ int v = -1; // 從當前不在最小生成樹集合中節點中找到dist最小的節點 for(int u = 0; u < n; u++){ if(!used[u] && (v == -1 || dist[u] < dist[v])){ v = u; } } if(v == -1) break; used[v] = true; res += dist[v]; // 根據最小節點和其它點的邊權值更新dist for(int u = 0; u < n; u++){ if(used[u]) continue; dist[u] = min(dist[u], cost[v][u]); } } }
三、總結
Prim演算法是一種十分常用而且實用的最小生成樹演算法,它的關鍵在於通過不斷加入當前最小的邊,建立一個連通的最小生成樹。雖然演算法的時間複雜度為O(n2),但在普通場景下的效率較高。如果遇到複雜的問題,開發者需要合理運用數據結構或者其他優化手段來提升演算法的效率。
原創文章,作者:OESBO,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/331583.html