最小生成树算法详解.ppt
- 格式:ppt
- 大小:757.01 KB
- 文档页数:8
最⼩⽣成树(Kruskal和Prim算法)关于图的⼏个概念定义:关于图的⼏个概念定义:连通图:在⽆向图中,若任意两个顶点vi与vj都有路径相通,则称该⽆向图为连通图。
强连通图:在有向图中,若任意两个顶点vi与vj都有路径相通,则称该有向图为强连通图。
连通⽹:在连通图中,若图的边具有⼀定的意义,每⼀条边都对应着⼀个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通⽹。
⽣成树:⼀个连通图的⽣成树是指⼀个连通⼦图,它含有图中全部n个顶点,但只有⾜以构成⼀棵树的n-1条边。
⼀颗有n个顶点的⽣成树有且仅有n-1条边,如果⽣成树中再添加⼀条边,则必定成环。
最⼩⽣成树:在连通⽹的所有⽣成树中,所有边的代价和最⼩的⽣成树,称为最⼩⽣成树。
构造最⼩⽣成树的准则有3条:(1)必须只使⽤该⽹络中的边来构造最⼩⽣成树。
(2)必须使⽤且仅使⽤n-1条边来连接⽹络中的n个顶点。
(3)不能使⽤产⽣回路的边。
下⾯介绍两种求最⼩⽣成树算法1 Prim(普利姆算法)算法--加点法此算法可以称为“加点法”,每次迭代选择代价最⼩的边对应的点,加⼊到最⼩⽣成树中。
算法从某⼀个顶点s开始,逐渐长⼤覆盖整个连通⽹的所有顶点。
Prim算法从任意⼀个顶点开始,每次选择⼀个与当前顶点集最近的⼀个顶点,并将两顶点之间的边加⼊到树中。
Prim算法在找当前最近顶点时使⽤到了贪婪算法。
实现过程:5int logo[1010];///⽤0和1来表⽰是否被选择过6int map1[1010][1010];7int dis[1010];///记录任意⼀点到这⼀点的最近的距离8int n,m;9int prim()10 {11int i,j,now;12int sum=0;13for(i=1;i<=n;i++)///初始化14 {15 dis[i]=MAX;16 logo[i]=0;17 }18for(i=1;i<=n;i++)19 {20 dis[i]=map1[1][i];21 }22 dis[1]=0;23 logo[1]=1;24for(i=1;i<n;i++)///循环查找25 {26 now=MAX;27int min1=MAX;28for(j=1;j<=n;j++)29 {30if(logo[j]==0&&dis[j]<min1)31 {32 now=j;33 min1=dis[j];34 }35 }36if(now==MAX)///防⽌不成图37 {38break;39 }40 logo[now]=1;41 sum=sum+min1;42for(j=1;j<=n;j++)///填⼊新点后更新最⼩距离,到顶点集的距离43 {44if(logo[j]==0&&dis[j]>map1[now][j])45 {46 dis[j]=map1[now][j];47 }48 }49 }50if(i<n)51 {52 printf("?\n");53 }54else55 {56 printf("%d\n",sum);57 }58 }59int main()60 {61while(scanf("%d%d",&m,&n)!=EOF)///n是点数62 {63if(m==0)64 {65break;66 }67 memset(map1,0x3f3f3f3f,sizeof(map1));///map是邻接矩阵储存图的信息68for(int i=0;i<m;i++)69 {70int a,b,c;71 scanf("%d%d%d",&a,&b,&c);72if(c<map1[a][b])///防⽌出现重边73 {74 map1[a][b]=map1[b][a]=c;75 }76 }77 prim();78 }79return0;80 }邻接表实现:1 #include<stdio.h>2 #include<string.h>3 #include<vector>4 #include<algorithm>5#define INF 0x3f3f3f3f6using namespace std;7struct node8 {9int end;///终点10int power;///权值11 } t;12int n;///n为点数13 vector<node>q[500001];///邻接表储存图的信息14int dis[500001];///距离15int vis[500001];///标记数组16void prime()17 {18int i,len,j,pos,sum,start;19 memset(vis,0,sizeof(vis));20 sum=0;21 start=1;///任意取起点22for(i=0; i<=n; i++)23 {24 dis[i]=INF;25 }26 len=q[start].size();27for(i=0; i<len; i++)///从任意起点开始的dis数组更新28 {29if(q[start][i].power<dis[q[start][i].end])30 {31 dis[q[start][i].end]=q[start][i].power;32 }33 }34 vis[start]=1;35for(j=0; j<n-1; j++)36 {37int pos,min=INF;38for(i=1; i<=n; i++)39 {40if(vis[i]!=0&&dis[i]<min)41 {42 min=dis[i];43 pos=i;///找到未访问节点中权值最⼩的44 }45 }46if(pos==INF)///防⽌不成图47 {48break;49 }50 vis[pos]=1;51 sum=sum+min;52 len=q[pos].size();///再次更新dis数组53for(j=0; j<len; j++)54 {55if(vis[q[pos][j].end]==0&&dis[q[pos][j].end]>q[pos][j].power)56 {57 dis[q[pos][j].end] = q[pos][j].power;58 }59 }60 }61if(j<n)62 {63 printf("?\n");64 }65else66 {67 printf("%d\n",sum);68 }69 }70int main()71 {72int m,i;73int begin,end,power;74int a,b;75while(scanf("%d%d",&n,&m)!=EOF)76 {77for(i=0; i<=n; i++)78 {79 q[i].clear();///将victor数组清空80 }81for(i=0; i<m; i++)82 {83 scanf("%d%d%d",&begin,&end,&power);///输⼊84 t.end=end;85 t.power=power;86 q[begin].push_back(t);87 t.end=begin;///⽆向图88 t.power=power;89 q[end].push_back(t);90 }91 prime();92 }93return0;94 }这⾥再给出⼀个没有使⽤标记数组的代码:int prim(int s){int i,j,sum=0;int now;for(i=1;i<=n;i++){closest[i]=INT_MAX;}for(i=1;i<=n;i++){closest[i]=map[s][i];}closest[s]=0;for(i=1;i<n;i++)//这⾥的i代表的是边数,有n个点就会有n-1条边{int min=INT_MAX;for(j=1;j<=n;j++){if(closest[j]&&closest[j]<min){min=closest[j];now=j;//找到所需的最⼩边}}sum+=min;closest[now]=0;//将找到的边加⼊到最⼩⽣成树之中for(j=1;j<=n;j++)//找到新的点加⼊已选点集合之后,更新该点到未选点集合的距离{if(map[now][j]&&map[now][j]<closest[j]){closest[j]=map[now][j];}}}return sum;}2 Kruskal(克鲁斯卡尔)算法--加边法1.概览 Kruskal算法是⼀种⽤来寻找最⼩⽣成树的算法,在剩下的所有未选取的边中,找最⼩边,如果和已选取的边构成回路,则放弃,选取次⼩边。