以前以爲自己用的生成最小生成樹的方法是Prim算法,今天自己拜讀了《數據結構與算法分析》之後才知道自己有多愚蠢,原來以前一直用的是KrusKal算法。。。。。。
今天好好說道說道這兩個算法:
KrusKal算法用於稀疏圖,貪心策略,連續的按照最小的權值選擇邊。
Prim算法用於稠密圖,也是貪心策略,每次取離小生成樹最近的點作爲生成樹的心點,併入生成樹內生成新的小生成樹,知道所有節點均被納入生成樹後結束。
這兩種方法均可得到點點之間路徑最短的聯通圖。
例如這個例子:
用Prim算法的過程:
用KrusKal算法的過程:
下面我編碼來實現Prim算法:
(測試數據均是圖一)
- #include<stdio.h>
- #include<string.h>
- #define INF 0x11111110
- int map[2010][2010],Tree[2010];
- int closeset[2010];//節點依附的點(就是與生成樹裏那個點鏈接)
- int lowcost[2010];//節點到生成樹的最短距離
- int sum=0;
- void Prim(int n)
- {
- int i,j,k;
- int min;
- Tree[1]=1;//先把 1放在生成樹裏
- for(i=1;i<=n;i++)
- {
- lowcost[i]=map[i][1];//所有節點與生成樹的最短距離初始化
- closeset[i]=1;//i依附於誰 (初始化都依附於1)
- }
- for(j=1;j<=n-1;j++)//n個節點有n-1條邊
- {
- min=INF;
- //找到離生成樹最近的點
- for(i=1;i<=n;i++)
- {
- if(!Tree[i]&&lowcost[i]<min)
- {
- min=lowcost[i];
- k=i;
- }
- }
- printf(”鏈接%d %d 距離爲%d\n”,k,closeset[k],min);
- sum+=min;
- Tree[k]=1; //把 k併入生成樹
- //找到離新加入的點最近的點,更新那個點距生成樹的距離
- for(i=1;i<=n;i++)
- {
- if(!Tree[i]&&map[k][i]<lowcost[i])
- {
- lowcost[i]=map[k][i];//更新最小值
- closeset[i]=k;//記錄新的依附點
- }
- }
- }
- }
- int main()
- {
- int i,j,n,m,x,y,z;
- while(scanf(“%d %d”,&n,&m)!=EOF)
- {
- memset(Tree,0,sizeof(Tree));//用來記錄節點是否在生成樹中
- memset(map,INF,sizeof(map));
- for(i=0;i<m;i++)
- {
- scanf(”%d %d %d”,&x,&y,&z);
- map[x][y]=z;
- map[y][x]=z;
- }
- printf(”生成樹的合成順序:\n”);
- Prim(n);
- printf(”生成樹的最短距離:%d\n”,sum);
- }
- return 0;
- }
#include<stdio.h>
輸入:
7 12
1 4 1
1 2 2
1 3 4
2 4 3
2 5 10
3 4 2
3 5 6
6 4 8
6 7 1
5 4 7
5 7 6
7 4 4
輸出:
生成樹的合成順序:
鏈接4 1 距離爲1
鏈接2 1 距離爲2
鏈接3 4 距離爲2
鏈接7 4 距離爲4
鏈接6 7 距離爲1
鏈接5 3 距離爲6
生成樹的最短距離:16
下面我編碼來實現KrusKal算法:
- #include<iostream>
- #include<algorithm>
- using namespace std;
- struct node
- {
- int a,b;
- int len;
- }per[5000];
- int cmp(node x,node y)
- {
- if(x.len!=y.len) return x.len<y.len;
- }
- int main()
- {
- int i,sum,n,m,num;
- int flag[200];
- while(scanf(“%d %d”,&n,&m),n!=0)
- {
- for(i=0;i<m;i++)
- {
- scanf(”%d%d%d”,&per[i].a,&per[i].b,&per[i].len);
- }
- sort(per,per+m,cmp);
- for(i=1;i<=n;i++) flag[i]=1;
- flag[per[0].a]=0;
- sum=0;
- for(i=0;i<m;i++)
- {
- num=flag[per[i].a]+flag[per[i].b];
- if(num==1)
- {
- printf(”鏈接%d %d 長度%d\n”,per[i].a,per[i].b,per[i].len);
- sum+=per[i].len;
- flag[per[i].a]=flag[per[i].b]=0;
- i=0;
- }
- }
- printf(”生成樹路徑總和%d\n”,sum);
- }
- return 0;
- }
#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
int a,b;
int len;
}per[5000];
int cmp(node x,node y)
{
if(x.len!=y.len) return x.len<y.len;
}
int main()
{
int i,sum,n,m,num;
int flag[200];
while(scanf("%d %d",&n,&m),n!=0)
{
for(i=0;i<m;i++)
{
scanf("%d%d%d",&per[i].a,&per[i].b,&per[i].len);
}
sort(per,per+m,cmp);
for(i=1;i<=n;i++) flag[i]=1;
flag[per[0].a]=0;
sum=0;
for(i=0;i<m;i++)
{
num=flag[per[i].a]+flag[per[i].b];
if(num==1)
{
printf("鏈接%d %d 長度%d\n",per[i].a,per[i].b,per[i].len);
sum+=per[i].len;
flag[per[i].a]=flag[per[i].b]=0;
i=0;
}
}
printf("生成樹路徑總和%d\n",sum);
}
return 0;
}
輸入
7 12
1 4 1
1 2 2
1 3 4
2 4 3
2 5 10
3 4 2
3 5 6
6 4 8
6 7 1
5 4 7
5 7 6
7 4 4
輸出:
鏈接6 7 長度1
鏈接7 4 長度4
鏈接1 4 長度1
鏈接3 4 長度2
鏈接1 2 長度2
鏈接5 7 長度6
生成樹路徑總和16
區別一目瞭然……..