Prim演算法生成最小生成樹詳解
阿新 • • 發佈:2019-01-01
建立一個low陣列代表相應每個節點連線所需最小費用,一個vis陣列標記相應節點是否連線過。
low陣列的初始值用節點1到其餘各點的費用來填充
如該圖所示low陣列的初始值應該為:
0 | 6 | 3 | 4 | 5 |
以上便是1號節點對於其他各點的費用
接下來在low陣列中尋找最小值 為3號節點費用為3最小。
接著以3號節點為起始點更新
0 | 6 | 3 | 4 | 5 |
接下來在low陣列中尋找最小值 為4號節點費用為4最小。
同理以4號節點為起始點更新
0 | 6 | 3 | 4 | 2 |
這裡可以看到第三次更新時便改變了連線5號節點所需的最小費用,值得注意的是low陣列的值每次都由最小費用的節點優化而來,因此重新選中一個節點時,在它進行優化之前,low陣列的值必定是最小值(因為之前每一個節點前輩都已經優化過了^_^)
總結一下便是:
第一步:將所有一號節點可以達到的點所需費用加入low陣列作為相應每一個點連線所需的假想最小費用
第二步:不斷搜尋low陣列中沒被確定(vis陣列標記)的最小值,然後通過這個節點去更新(優化)low陣列
Prim演算法在選取起始點使用貪心,而對low陣列優化的全過程則有DP的影子。
我們可以看出Prim演算法需要列舉節點,所以相對於Kruskal演算法來說稠密圖更適合Prim演算法,而Kruskal演算法解決稀疏圖比Prim演算法更有效率。
克魯斯卡爾演算法->http://blog.csdn.net/x__1998/article/details/79503583
#include <stdio.h> #include <string.h> #define inf 0x3f3f3f3f int map[1001][1001],low[1001],vis[1001]; int Prime(int cost[][1001],int n) { int i,j,ans=0; printf ("low陣列的變化過程如下:\n"); printf ("0 "); for (i = 2;i <= n; i++){//先將節點1作為起始點將所有的費用暫時放入low陣列 low[i] = cost[1][i]; printf ("%d ",low[i]); } printf ("\n"); vis[1]=1;//標記1節點表示1節點所需的費用固定 for (i = 2;i <= n; i++){//還有n-1個點沒有確定最小連線費用 int min = inf,p=-1; for (j = 1;j <= n; j++){//找出費用最小的點的話,那該節點連線所需費用一定最小 if (!vis[j]&&min > low[j]){ min = low[j]; p = j; } printf ("%d ",low[j]); } printf ("\n"); if(p == -1) return NULL; ans += low[p]; vis[p] = 1;//確定這個點所需的費用已經最小將其加入佇列 for (j = 1;j <= n; j++){//維護low陣列,以當前找到的新節點作為起點,更新各節點的最小費用 //否則上一行找到的最小值節點無法確定就是最小的,其包含了動態規劃的思想 if (!vis[j] && cost[p][j] < low[j]){//這就是優化的核心語句了 low[j] = cost[p][j]; } } } printf ("最小權值:%d\n",ans); } int main () { int m,n; printf ("請輸入節點數以及邊數:\n"); while (~scanf ("%d%d",&m,&n)){ memset(map,inf,sizeof map); memset(vis,0,sizeof vis); memset(low,0,sizeof low); int i,j,a,b,w; printf ("請輸入邊的權值:\n"); for (i = 1;i <= n; i++){ scanf ("%d%d%d",&a,&b,&w); if (w < map[a][b]){//這裡為了防止輸入的資料重複輸入同兩個點之間的權值 map[a][b] = w; map[b][a] = w; } } Prime(map,m); printf ("----------------------\n請輸入節點數以及邊數:\n"); } return 0; }