分别利用prim算法和kruskal算法实现求图的最小生成树
- 格式:doc
- 大小:35.50 KB
- 文档页数:9
求无向图的最小生成树算法——Prim与Kruskal一.Prim算法1.算法思想对于图G=(V,E),用Prim算法求最小生成树T=(S,TE)的流程如下① 初始化:设S、TE为空集,任选节点K加入S。
② 选取一条权值最小的边(X,Y),其中X∈S,且not (Y∈S)即,选取一条权值最小的、连接着S中一点与S外一点的边。
将Y加入S中,边(X,Y)加入TE中重复② 直到V=S即所有G中的点都在S中,此时的T为G的最小生成树。
由此流程可见,Prim算法求最小生成树时任何时候的T都是一颗树。
2.实现显然,Prim算法的主要运行时间花在过程②的选边中。
看起来复杂度是O(VE)=O(V^3)不是么,效率也太低了吧……为了比较快速地选边,我们用两个数组lowcost、closest动态地维护每一个点到S的最短距离。
在某一状态下,lowcost[i]表示所有与i相连且另一端点在S中的边中的权值最小值,closest[i]表示在S中且与i相连的点中与i之间距离最小的点。
显然,lowcost[i]=w(i,closest[i])。
需要注意的是两个数组记录的都是边而不是路径。
若i没有边直接连向S,则lowcost[i]=∞。
另外,若i已在S 中,则lowcost[i]=0。
设出发点为x。
初始时对于任意k∈V,closest[k]=x,lowcost[k]=w(k,x)【w(i,j)表示i、j间的距离。
初始化时,若两点间没有边则w(i,j)赋为一个足够大的整数(如maxint),并且所有点到自身的距离赋为0,即w(i,i)=0】每一次找出lowcost中不为0的最小值lowcost[i],然后把i加入S(即lowcost[i]:=0),然后对于图中所有点k,若w(k,i)<lowcost[k],则把lowcost[k]赋为w(k,i),把closest[k]赋为i。
【由于s中所有点的lowcost都为0,所以只影响到s以外的点】以上操作重复|V|-1次结束。
最⼩⽣成树问题---Prim算法与Kruskal算法实现(MATLAB语⾔实现) 2015-12-17晚,复习,甚是⽆聊,阅《复杂⽹络算法与应⽤》⼀书,得知最⼩⽣成树问题(Minimum spanning tree)问题。
记之。
何为树:连通且不含圈的图称为树。
图T=(V,E),|V|=n,|E|=m,下列关于树的说法等价:T是⼀个树。
T⽆圈,且m=n-1。
T连通,且m=n-1。
T⽆圈,但每加⼀新边记得到唯⼀⼀个圈。
T连通,但任舍去⼀边就不连通。
T中任意两点,有唯⼀道路相连。
何为⽣成树:若图G=(V,E)的⽣成⼦图是⼀棵树,则称该树为图G的⽣成树,也称⽀撑树,简称为图G的数。
图G中属于⽣成树的边称为数枝(Branch)。
何为最⼩⽣成树:连通图G=(V,E),每条边上有⾮负权L(e)。
⼀棵树上所有树枝权的总和,称为这个⽣成树的权。
具有最⼩权的⽣成树称为最⼩⽣成树,也就是说最⼩⽀撑树,简称最⼩树。
私以为,两种算法其实都是贪⼼,所以需要严格的证明。
由于最近时间零散、数学久置未学、对算法领域没有系统了解。
所以不进⾏深⼊探讨(也就是说证明),仅以⼀个简单实例做⼀个⼊门级的了解。
Prim算法: 给定连通赋权图G=(V,E,W),其中W为邻接矩阵,设两个集合P和Q,其中P⽤于存放G的最⼩⽣成树中的节点,集合Q存放G的最⼩G的最⼩⽣成树中的边。
另集合P的初值为P={v1}(假设构造最⼩⽣成树时从v1出发),集合Q的初值为P={空集}。
(1)P = {v1},Q = {空集}; (2)while P ~= Q 找到最⼩边pv,其中p∈P,v∈V-P; P = P + {v}; Q = Q + {pv}; end Kruskal算法 (1)选e1∈E(G),使得w(e1) = min(选e1的权值最⼩)。
(2)e1,e2,...,e i已选好,则从E(G)-{e1,e2,...,e i}中选取e i+1,使得G[{e1,e2,...,e i,e i+1}]中⽆圈,且,w(e i+1) = min。
的最小生成树算法Prim与Kruskal算法的比较Prim算法和Kruskal算法都是常用的最小生成树算法,它们可以在给定的加权连通图中找到连接所有节点的最小权重边集合。
然而,这两种算法在实现细节和时间复杂度上有所不同。
本文将对Prim算法和Kruskal算法进行比较,并讨论它们的优缺点以及适用场景。
一、Prim算法Prim算法是一种贪心算法,它从一个起始节点开始,逐步扩展最小生成树的边集合,直到包含所有节点为止。
具体步骤如下:1. 选取一个起始节点作为最小生成树的根节点。
2. 在最小生成树的边集合中寻找与当前树集合相连的最小权重边,并将这条边添加到最小生成树中。
3. 将新添加的节点加入到树集合中。
4. 重复步骤2和3,直到最小生成树包含所有节点为止。
Prim算法的时间复杂度为O(V^2),其中V是节点的个数。
这是因为在每轮迭代中,需要从树集合以外的节点中找到与树集合相连的最小权重边,而在最坏情况下,可能需要检查所有的边。
二、Kruskal算法Kruskal算法是一种基于边的贪心算法,它按照边的权重从小到大的顺序依次选择边,并判断是否加入最小生成树中。
具体步骤如下:1. 初始化一个空的最小生成树。
2. 将所有边按照权重从小到大进行排序。
3. 依次检查每条边,如果这条边连接了两个不同的树(即不会形成环),则将这条边加入到最小生成树中。
4. 重复步骤3,直到最小生成树包含所有节点为止。
Kruskal算法使用并查集数据结构来快速判断连通性,时间复杂度为O(ElogE),其中E是边的个数。
排序边的时间复杂度为O(ElogE),而对每条边进行判断和合并操作的时间复杂度为O(E)。
三、比较与总结1. 时间复杂度:Prim算法的时间复杂度为O(V^2),而Kruskal算法的时间复杂度为O(ElogE)。
因此,在边的数量较大的情况下,Kruskal 算法的效率优于Prim算法。
2. 空间复杂度:Prim算法需要维护一个大小为V的优先队列和一个大小为V的布尔数组,而Kruskal算法需要维护一个大小为V的并查集。
最⼩⽣成树的Prim算法以及Kruskal算法的证明Prime算法的思路:从任何⼀个顶点开始,将这个顶点作为最⼩⽣成树的⼦树,通过逐步为该⼦树添加边直到所有的顶点都在树中为⽌。
其中添加边的策略是每次选择外界到该⼦树的最短的边添加到树中(前提是⽆回路)。
Prime算法的正确性证明:引理1:对于连通图中的顶点vi,与它相连的所有边中的最短边⼀定是属于最⼩⽣成树的。
引理2:证明:假设最⼩⽣成树已经建成;(vi, vj)是连接到顶点vi的最短边,在最⼩⽣成树中取出vi,断开连接到vi的边,则⽣成树被拆分成1、顶点vi2、顶点vj所在的连通分量(单独⼀个顶点也看作⼀个独⽴的连通分量)3、其余若⼲个连通分量(个数⼤于等于0)三个部分现在要重建⽣成树,就要重新连接之前被断开的各边虽然不知道之前被断开的都是哪⼏条边,但是可以通过这样⼀个简单的策略来重建连接:将vi分别以最⼩的成本逐个连接到这若⼲个互相分离的连通分量;具体来说,就是要分别遍历顶点vi到某个连通分量中的所有顶点的连接,然后选择其中最短的边来连接vi和该连通分量;⽽要将vi连接到vj所在的连通分量,显然通过边(vi, vj)连接的成本最低,所以边(vi, vj)必然属于最⼩⽣成树(如果连接到vi的最短边不⽌⼀条,只要任意挑选其中的⼀条(vi, vj)即可,以上的证明对于这种情况同样适⽤)。
这样我们就为原来只有⼀个顶点vi的⼦树添加了⼀个新的顶点vj及新边(vi, vj);接下来只要将这棵新⼦树作为⼀个连通⼦图,并且⽤这个连通⼦图替换顶点vi重复以上的分析,迭代地为⼦树逐个地添加新顶点和新边即可。
Kruskal算法:通过从⼩到⼤遍历边集,每次尝试为最⼩⽣成树加⼊当前最短的边,加⼊成功的条件是该边不会在当前已构建的图中造成回路,当加⼊的边的数⽬达到n-1,遍历结束。
Kruskal算法的正确性证明:Kruskal算法每次为当前的图添加⼀条不会造成回路的新边,其本质是逐步地连接当前彼此分散的各个连通分量(单个顶点也算作⼀个连通分量),⽽连接的策略是每次只⽤最⼩的成本连接任意两个连通分量。
最⼩⽣成树---普⾥姆算法(Prim算法)和克鲁斯卡尔算法(Kruskal算法)最⼩⽣成树的性质:MST性质(假设N=(V,{E})是⼀个连通⽹,U是顶点集V的⼀个⾮空⼦集,如果(u,v)是⼀条具有最⼩权值的边,其中u属于U,v属于V-U,则必定存在⼀颗包含边(u,v)的最⼩⽣成树)普⾥姆算法(Prim算法)思路:以点为⽬标构建最⼩⽣成树1.将初始点顶点u加⼊U中,初始化集合V-U中各顶点到初始顶点u的权值;2.根据最⼩⽣成树的定义:从n个顶点中,找出 n - 1条连线,使得各边权值最⼩。
循环n-1次如下操作:(1)从数组lowcost[k]中找到vk到集合U的最⼩权值边,并从数组arjvex[k] = j中找到该边在集合U中的顶点下标(2)打印此边,并将vk加⼊U中。
(3)通过查找邻接矩阵Vk⾏的各个权值,即vk点到V-U中各顶点的权值,与lowcost的对应值进⾏⽐较,若更⼩则更新lowcost,并将k存⼊arjvex数组中以下图为例#include<bits/stdc++.h>using namespace std;#define MAXVEX 100#define INF 65535typedef char VertexType;typedef int EdgeType;typedef struct {VertexType vexs[MAXVEX];EdgeType arc[MAXVEX][MAXVEX];int numVertexes, numEdges;}MGraph;void CreateMGraph(MGraph *G) {int m, n, w; //vm-vn的权重wscanf("%d %d", &G->numVertexes, &G->numEdges);for(int i = 0; i < G->numVertexes; i++) {getchar();scanf("%c", &G->vexs[i]);}for(int i = 0; i < G->numVertexes; i++) {for(int j = 0; j < G->numVertexes; j++) {if(i == j) G->arc[i][j] = 0;else G->arc[i][j] = INF;}}for(int k = 0; k < G->numEdges; k++) {scanf("%d %d %d", &m, &n, &w);G->arc[m][n] = w;G->arc[n][m] = G->arc[m][n];}}void MiniSpanTree_Prim(MGraph G) {int min, j, k;int arjvex[MAXVEX]; //最⼩边在 U集合中的那个顶点的下标int lowcost[MAXVEX]; // 最⼩边上的权值//初始化,从点 V0开始找最⼩⽣成树Tarjvex[0] = 0; //arjvex[i] = j表⽰ V-U中集合中的 Vi点的最⼩边在U集合中的点为 Vjlowcost[0] = 0; //lowcost[i] = 0表⽰将点Vi纳⼊集合 U ,lowcost[i] = w表⽰ V-U中 Vi点到 U的最⼩权值for(int i = 1; i < G.numVertexes; i++) {lowcost[i] = G.arc[0][i];arjvex[i] = 0;}//根据最⼩⽣成树的定义:从n个顶点中,找出 n - 1条连线,使得各边权值最⼩for(int i = 1; i < G.numVertexes; i++) {min = INF, j = 1, k = 0;//寻找 V-U到 U的最⼩权值minfor(j; j < G.numVertexes; j++) {// lowcost[j] != 0保证顶点在 V-U中,⽤k记录此时的最⼩权值边在 V-U中顶点的下标if(lowcost[j] != 0 && lowcost[j] < min) {min = lowcost[j];k = j;}}}printf("V[%d]-V[%d] weight = %d\n", arjvex[k], k, min);lowcost[k] = 0; //表⽰将Vk纳⼊ U//查找邻接矩阵Vk⾏的各个权值,与lowcost的对应值进⾏⽐较,若更⼩则更新lowcost,并将k存⼊arjvex数组中for(int i = 1; i < G.numVertexes; i++) {if(lowcost[i] != 0 && G.arc[k][i] < lowcost[i]) {lowcost[i] = G.arc[k][i];arjvex[i] = k;}}}int main() {MGraph *G = (MGraph *)malloc(sizeof(MGraph));CreateMGraph(G);MiniSpanTree_Prim(*G);}/*input:4 5abcd0 1 20 2 20 3 71 2 42 3 8output:V[0]-V[1] weight = 2V[0]-V[2] weight = 2V[0]-V[3] weight = 7最⼩总权值: 11*/时间复杂度O(n^2)克鲁斯卡尔算法(Kruskal算法)思路:以边为⽬标进⾏构建最⼩⽣成树在边集中依次寻找最⼩权值边,若构建是不形成环路(利⽤parent数组记录各点的连通分量),则将其添加到最⼩⽣成树中。
采用普里姆算法和克鲁斯卡尔算法,求最小生成树普利姆算法(Prim's Algorithm)和克鲁斯卡尔算法(Kruskal's Algorithm)是求解最小生成树的两种常用方法。
最小生成树是指连接图中所有节点,且边的权重和最小的树。
这两种算法各有特点,在不同的场景中使用。
1.普利姆算法:适用于边稠密的图普利姆算法是一种贪心算法,从一个节点开始,不断选择与当前树相连的、权重最小的边,并将该边连接的节点加入树中,直到所有节点都被遍历完。
这样就得到了最小生成树。
以下是普利姆算法的伪代码:1.创建一个空的树,用于保存最小生成树2.选择一个起始节点,将其加入树中3.从树中已有的节点出发,找到与树相连的边中权重最小的边4.将找到的边连接的节点加入树中5.重复步骤3和4,直到所有节点都加入树中普利姆算法的时间复杂度为O(ElogV),其中E为边的数量,V为节点的数量。
2.克鲁斯卡尔算法:适用于边稀疏的图克鲁斯卡尔算法是一种基于排序和并查集的贪心算法,按照边的权重从小到大的顺序选择,并判断是否会构成环。
如果不会构成环,则选择该边,并将其加入最小生成树中,直到所有节点都被连接。
以下是克鲁斯卡尔算法的伪代码:1.创建一个空的树,用于保存最小生成树2.将所有边按权重从小到大排序3.创建一个并查集,用于判断边是否会构成环4.遍历排序后的边,对于每条边,判断其连接的两个节点是否属于同一个集合(即是否会构成环)5.如果不会构成环,则选择该边,并将其加入树中,同时将该边连接的两个节点合并到同一个集合中6.重复步骤4和5,直到所有节点都连接在一起克鲁斯卡尔算法的时间复杂度为O(ElogE),其中E为边的数量。
这两种算法的应用场景有所不同。
如果要求解的图是边稠密的(即边的数量接近节点数量的平方),则使用普利姆算法更为高效。
因为普利姆算法的时间复杂度与边的数量有关,所以处理边稠密的图会更快一些。
而对于边稀疏的图(即边的数量接近节点数量的线性),克鲁斯卡尔算法更加适用,因为它的时间复杂度与边的数量有关。
采用普里姆算法和克鲁斯卡尔算法,求最小生成树什么是最小生成树?最小生成树是图论中的一个重要概念,它是指在一个给定的无向连通图中,找到一棵树,使得这棵树连接图中的所有顶点,并且具有最小的权值总和。
最小生成树在很多实际问题中有着广泛的应用,比如城市规划、电力网络规划等。
普里姆算法:普里姆算法又称为“加点法”,它从一个初始随机点开始,逐渐往图中加入新的点,直到能够生成一棵包含所有节点的最小生成树。
1. 首先选择一个任意节点作为起始节点,加入最小生成树中。
2. 从已经加入最小生成树的节点中,选择一个与之相邻的节点并且不在最小生成树中的边,找到权值最小的边,将其加入最小生成树。
3. 重复第二步,直到最小生成树包含了所有的节点,即生成了一棵最小生成树。
克鲁斯卡尔算法:克鲁斯卡尔算法又称为“加边法”,它从原图的边集中选择权值最小的边,逐步加入生成树的边集中,直到遍历完所有的边,同时生成一棵最小生成树。
1. 首先把图中的所有边按照权值从小到大进行排序。
2. 依次遍历排序后的边,判断每一条边的两个顶点是否属于同一个连通分量。
3. 如果不属于同一个连通分量,将该边加入最小生成树的边集中,并将两个顶点所在的连通分量合并。
4. 重复第二步和第三步,直到遍历完所有的边或者最小生成树的边数达到图中节点数减一。
两种算法的比较:普里姆算法是从一个初始点开始,每次加入一个与最小生成树相连的具有最小权值的点,直到生成一棵最小生成树。
这种算法的时间复杂度为O(V^2),其中V表示图中的顶点数。
因此,普里姆算法适用于顶点数较少的情况。
克鲁斯卡尔算法是将边按照权值排序后逐步加入最小生成树的边集中。
这种算法的时间复杂度为O(ElogE),其中E表示图中的边数。
因此,克鲁斯卡尔算法适用于边数较少的情况。
从时间复杂度的角度来看,克鲁斯卡尔算法在边数较少的情况下更为高效,而普里姆算法在顶点数较少的情况下更为高效。
总结:最小生成树是一个在图论中非常重要且常用的概念,可以用于解决很多实际问题。
最⼩⽣成树(MST)Prim算法和Kruskal算法刚学完最⼩⽣成树,赶紧写写学习的⼼得(其实是怕我⾃⼰忘了)最⼩⽣成树概念:⼀个有 n 个结点的的⽣成树是原图的极⼩连通⼦图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。
就是说如果我们想把⼀张有n个点的图连接起来,那我们就只需要n-1条边(原因显然:就如同⼀条有n个点的线段,他们之间最少需要n-1条边连起来)最⼩⽣成树就是寻找值最⼩的这n-1个点,把他们加和。
⾸先,最⼩⽣成树最基本的算法是Prim和Kruskal算法Prim算法:算法分析&思想讲解:Prim算法采⽤“蓝⽩点”思想:⽩点代表已经进⼊最⼩⽣成树的点,蓝点代表未进⼊最⼩⽣成树的点。
Prim算法每次循环都将⼀个蓝点u变为⽩点,并且此蓝点u与⽩点相连的最⼩边权min[u]还是当前所有蓝点中最⼩的。
这样相当于向⽣成树中添加了n-1次最⼩的边,最后得到的⼀定是最⼩⽣成树。
Prim算法的好处就在于它与边⽆关,主要⽤于稠密图,复杂度为O(n^2),实⽤度不如Kruskal算法⾼代码介绍:(好像不可以直接⽤,有点问题)#include<iostream>#include<cstring>#include<cstdio>using namespace std;const int MAXN=5010;int t[MAXN][MAXN];bool b[MAXN];int MIN[MAXN];int main(){memset(b,false,sizeof(b));memset(t,127,sizeof(t));memset(MIN,127,sizeof(MIN)); //把每⼀条未赋值的边赋为较⼤的⼀个数int n,m;int ans=0;scanf("%d",&n);for(int i=1;i<=n;i++)t[i][i]=0;for(int i=1;i<=n;i++){ //邻接矩阵存图for (int j=1;j<=n;j++){ //不同问题存图⽅式不同cin>>t[i][j];}}MIN[1]=0;//先找点:for(int i=1;i<=n;i++){int x=0; //x为0 就是说⼀开始是从⼀个虚拟点开始的然后我们找与它相邻的边并且还没被找过的点for(int j=1;j<=n;j++){if(!b[j]&&MIN[j]<MIN[x]){ //我们以这⼀个点开始寻找与它相邻的最⼩的边x=j; //然后就标记这个点以便于接着⽤这个点继续往下找}}b[x]=true; //找完这个点后就变成⽩点,表⽰已找过//再扩边:for(int j=1;j<=n;j++){if(!b[j]&&MIN[j]>t[x][j]){ //这段代码就是给我们刚找到的X点的邻边赋实际值,这样在下次寻找X的最⼩边时就可以找到啦MIN[j]=t[x][j]; //所以说找点的代码就⽐较好理解了}}}for(int i=1;i<=n;i++){ans+=MIN[i];//求最⼩和}cout<<ans<<endl;return0;}知识扩展:本算法在移动通信、智能交通、移动物流、⽣产调度等物联⽹相关领域都有⼗分现实的意义,采⽤好的算法,就能节省成本提⾼效率。
最小生成树算法总结最小生成树是指在一个无向连通图中,找到一个子树,使得这棵子树中所有边的权值之和最小。
最小生成树可以用于最优化问题,例如道路铺设、网络布线等。
下面将介绍三种最小生成树算法:Prim算法、Kruskal算法、Boruvka算法。
1. Prim算法Prim算法是一种贪心算法,从一个点开始,每次添加连接到已有集合中的最小边,直到所有点都在同一个集合中。
可以用以下步骤描述Prim算法:(1) 选择一个起点,将该起点加入最小生成树的顶点集合,然后将该顶点相邻的边加入边集合中。
(2) 从边集合中找到权值最小的一条边,将该边对应的顶点加入最小生成树的顶点集合,同时将该顶点相邻的边加入边集合中。
(3) 重复上述步骤,直到所有顶点都在最小生成树的顶点集合中。
Prim算法的实现可以使用堆优化,时间复杂度为O(E + VlogV),其中E为边数,V为顶点数。
2. Kruskal算法Kruskal算法也是一种贪心算法,与Prim算法不同的是,Kruskal算法是按照边的权值从小到大依次添加,直到所有顶点都在同一个集合中。
可以用以下步骤描述Kruskal算法:(1) 将所有边按照权值从小到大排序。
(2) 依次取出排好序的边,如果该边所连接的两个顶点不在同一个集合中,就将这条边加入最小生成树的边集合中,并将这两个顶点合并到同一个集合中。
(3) 重复步骤(2),直到所有顶点都在同一个集合中。
Kruskal算法的实现可以使用并查集,时间复杂度为O(ElogE),其中E为边数。
3. Boruvka算法Boruvka算法是一种基于集合的分治算法,与Prim算法和Kruskal算法不同,Boruvka算法的时间复杂度是线性的。
可以用以下步骤描述Boruvka算法:(1) 对每个顶点建立单元素集合。
(2) 对每个集合,选择与该集合相连的最小权值的边,将这些边添加到最小生成树的边集合中,并将这些集合合并到同一个集合中。
(3) 如果只剩下一个集合,算法结束。
的最小生成树算法Prim和Kruskal算法Prim和Kruskal算法是求解最小生成树的两种常用算法。
最小生成树指的是在一个连通图中,找到一棵包含所有顶点的生成树,使得树上边的权重之和最小。
Prim算法基于贪心思想,从一个起始顶点开始,逐步向其他顶点扩展,每次选择权重最小的边连接已经选中的顶点和未选中的顶点。
具体步骤如下:1. 初始化一个空的集合S,用于存放已经选中的顶点。
2. 从图中任选一个顶点作为起始顶点,并将其加入集合S。
3. 重复以下步骤,直到集合S包含了所有顶点:a. 在未加入集合S的顶点中,找到与集合S中顶点相连的边中权重最小的边。
b. 将该边加入生成树中,并将与该边相连的顶点加入集合S。
4. 生成的树即为最小生成树。
Kruskal算法是基于边的权重排序的思想。
具体步骤如下:1. 初始化一个空的集合S,用于存放已经选中的边。
2. 将图中的所有边按照权重从小到大排序。
3. 重复以下步骤,直到生成的树中包含了所有顶点-1条边(其中顶点的数量为n):a. 从排序后的边列表中选取一条最小权重的边。
b. 若该边的两个顶点不在同一连通分量中,将该边加入生成树中。
c. 否则,舍弃该边,继续选择下一条边。
4. 生成的树即为最小生成树。
值得注意的是,在构建最小生成树时,如果图不是连通图,那么最小生成树就不存在。
Prim算法的时间复杂度为O(V^2),其中V表示顶点的数量。
在稠密图中效果较好。
而Kruskal算法的时间复杂度为O(ElogE),其中E表示边的数量。
在稀疏图中效果较好。
这两种算法在实际应用中都有一定的局限性。
Prim算法适合处理边稠密、顶点稀疏的图,而Kruskal算法适合处理边稀疏、顶点稀疏的图。
在选择使用算法时,需要根据具体问题的特点进行权衡。
最小生成树算法Prim和Kruskal算法在图论领域具有重要的地位,广泛应用于网络设计、电路布线、城市规划等领域。
通过构建最小生成树,可以在保证连通性的前提下,选择最经济、最高效的路径和连接方式,节约资源并提高效率。
一、树及生成树的基本概念树是无向图的特殊情况,即对于一个N 个节点的无向图,其中只有N-1条边,且图中任意两点间有且仅有一条路径,即图中不存在环,这样的图称为树,一般记为T。
树定义有以下几种表述:(1)、T 连通、无圈、有n 个结点,连通有n-1条边;(2)、T 无回路,但不相邻的两个结点间联以一边,恰得一个圈;(3)、T 连通,但去掉任意一边,T 就不连通了(即在点集合相同的图中,树是含边数最少的连通图);(4)、T 的任意两个结点之间恰有一条初等链。
例如:已知有六个城市,它们之间要架设电话线,要求任意两个城市均可以互相通话,并且电话线的总长度最短。
若用六个点v 1…v 6代表这六个城市,在任意两个城市之间架设电话线,即在相应的两个点之间连一条边。
这样,六个城市的一个电话网就作成一个图。
任意两个城市之间均可以通话,这个图必须是连通图,且这个图必须是无圈的。
否则,从圈上任意去掉一条边,剩下的图仍然是六个城市的一个电话网。
图5-6是一个不含圈的连通图,代表了一个电话线网。
生成树(支撑树)定义:如果图G’是一棵包含G 的所有顶点的树,则称G’是G 的一个支撑树或生成树。
例如,图5-7b 是图5-7a 的一个支撑树。
定理:一个图G 有生成树的条件是G 是连通图。
证明:必要性显然;充分性:设图G 是连通的,若G 不含圈,则按照定义,G 是一个树,从而G 是自身的一个生成树。
若G 含圈,则任取G 的一个圈,从该圈中任意去掉一条边,得到图G 的一生成子图G 1。
若G 1不含圈,则G 1是G 的一个生成树。
若G 1仍然含圈,则任取G 1的一个圈,再从圈中任意去掉一条边,得到图G 的一生成子图G 2。
依此类推,可以得到图G 的一个生成子图G K ,且不含圈,从而G K 是一个生成树。
寻找连通图生成树的方法:破圈法:从图中任取一个圈,去掉一条边。
再对剩下的图重复以上步骤,直到不含圈时为止,这样就得到一个生成树。
取一个圈(v 1,v 2,v 3,v 1),在一个圈中去掉边e 3。
Prim算法和Kruskal算法Prim算法和Kruskal算法都能从连通图找出最小生成树。
区别在于Prim算法是挨个找,而Kruskal是先排序再找。
一、Prim算法:Prim算法实现的是找出一个有权重连通图中的最小生成树,即:具有最小权重且连接到所有结点的树。
(强调的是树,树是没有回路的)。
Prim算法是这样来做的:首先以一个结点作为最小生成树的初始结点,然后以迭代的方式找出与最小生成树中各结点权重最小边,并加入到最小生成树中。
加入之后如果产生回路则跳过这条边,选择下一个结点。
当所有结点都加入到最小生成树中之后,就找出了连通图中的最小生成树了。
Prim算法最小生成树查找过程:注意:若候选轻边集中的轻边不止一条,可任选其中的一条扩充到T中。
连通网的最小生成树不一定是惟一的,但它们的权相等。
【例】在上图(e)中,若选取的轻边是(2,4)而不是(2,1)时,则得到如图(h)所示的另一棵MST。
算法特点该算法的特点是当前形成的集合T始终是一棵树。
将T中U和TE分别看作红点和红边集,V-U看作蓝点集。
算法的每一步均是在连接红、蓝点集的紫边中选择一条轻边扩充进T中。
MST性质保证了此边是安全的。
T从任意的根r开始,并逐渐生长直至U=V,即T 包含了C中所有的顶点为止。
MST性质确保此时的T是G的一棵MST。
因为每次添加的边是使树中的权尽可能小,因此这是一种"贪心"的策略。
算法分析该算法的时间复杂度为O(n2)。
与图中边数无关,该算法适合于稠密图。
算法演示:/sjjg/DataStructure/DS/web/flashhtml/prim.htm二、Kruskal算法:Kruskal算法与Prim算法的不同之处在于,Kruskal在找最小生成树结点之前,需要对所有权重边做从小到大排序。
将排序好的权重边依次加入到最小生成树中,如果加入时产生回路就跳过这条边,加入下一条边。
当所有结点都加入到最小生成树中之后,就找出了最小生成树。
最小生成树的方法
最小生成树(Minimum Spanning Tree)是指在一个带权无向连通图中,找到一个包含所有顶点且总权值最小的树。
常用的方法有以下几种:
1. Prim算法(普里姆算法):从一个起始顶点开始,逐步扩展生成树,每次选择一个与当前生成树距离最小的顶点加入,直到所有顶点都被包含在生成树中。
2. Kruskal算法(克鲁斯卡尔算法):首先将图的所有边按照权值从小到大排序,然后依次选择权值最小的边加入生成树中,但要保证加入边后不会形成环,直到生成树中包含所有顶点,或者图中的所有边都被考虑过。
3. Boruvka算法(博鲁卡尔算法):将图的所有顶点分成多个不相交的集合,每个集合中的顶点组成一棵生成树,然后每次选择具有最小权值且连接两个不同集合的边加入生成树中,直到只剩下一个集合。
4. Jarnik算法(加尔尼克算法):也称为更改版的Prim算法,首先选择一个起始顶点加入生成树中,然后通过比较当前生成树中的顶点到其他顶点的距离,选择一个距离最小的顶点加入生成树,重复该过程直到所有顶点都被包含在生成树中。
这些方法都可以得到最小生成树,但在某些情况下,它们的效率和性能可能会不同。
选择合适的方法取决于具体的应用场景和图的特征。
最小生成树实验报告1.引言最小生成树(Minimum Spanning Tree,简称MST)是图论中的重要概念,在各个领域都有广泛的应用。
最小生成树是指在给定的加权连通图中,选择一个子集,使得该子集包含了所有的顶点,并且所有边的权值之和最小。
本实验主要目的是探讨最小生成树的算法并比较它们的效率和准确性。
2.实验方法本次实验使用Python编程语言实现了两种著名的最小生成树算法:Prim算法和Kruskal算法。
Prim算法是一种贪心算法,从一个顶点开始不断扩张集合,直到包含所有顶点,生成最小生成树。
Kruskal算法则是基于并查集的算法,将边按照权值排序后逐一加入生成树,同时要保证加入的边不会产生环路。
3.实验过程首先,我们从文件中读取了一张加权无向图的数据。
图的表示采用邻接矩阵的方式,即用一个二维数组来存储顶点之间的连接关系和权值。
读取完图的数据后,我们分别使用Prim算法和Kruskal算法求解最小生成树。
在Prim算法中,我们使用一个辅助数组来记录顶点是否已被访问过,然后从任意一个顶点开始,依次将与当前集合相邻的顶点加入,并选择权值最小的边。
直到所有顶点都被访问过,并形成了一个最小生成树。
在Kruskal算法中,我们首先将所有边按照权值从小到大进行排序。
然后,从权值最小的边开始,逐一将边加入生成树。
加入时,需要判断两个顶点是否在同一个连通分量中,以避免产生环路。
实验中,我们使用了Python中的heapq库来实现了堆排序,以加快Prim算法的运行速度。
4.实验结果经过实验,我们得到了图的最小生成树以及对应的权值。
实验数据显示,当图中顶点较少时,Prim算法和Kruskal算法几乎没有明显的差别。
但当图的规模增大时,Prim算法明显比Kruskal算法更快。
5.实验分析从实验结果可以看出,Prim算法和Kruskal算法都可以求解最小生成树,但在不同情况下它们的性能表现并不相同。
Prim算法适用于稠密图,因为它的时间复杂度与顶点的平方成正比;而Kruskal算法适用于稀疏图,因为它的时间复杂度与边的数量成正比。
JS使⽤Prim算法和Kruskal算法实现最⼩⽣成树之前都是看书,⼤部分也是c++的实现,但是搞前端不能忘了JS啊,所以JS实现⼀遍这两个经典的最⼩⽣成树算法。
⼀、权重图和最⼩⽣成树权重图:图的边带权重最⼩⽣成树:在连通图的所有⽣成树中,所有边的权重和最⼩的⽣成树本⽂使⽤的图如下:它的最⼩⽣成树如下:⼆、邻接矩阵邻接矩阵:⽤来表⽰图的矩阵就是邻接矩阵,其中下标表⽰顶点,矩阵中的值表⽰边的权重(或者有⽆边,⽅向等)。
本⽂在构建邻接矩阵时,默认Number.MAX_SAFE_INTEGER表⽰两个节点之间没有边,Number.MIN_SAFE_INTEGER表⽰当前节点没有⾃环。
代码如下:/*** 邻接矩阵* 值为顶点与顶点之间边的权值,0表⽰⽆⾃环,⼀个⼤数表⽰⽆边(⽐如10000)* */const MAX_INTEGER = Number.MAX_SAFE_INTEGER;//没有的边const MIN_INTEGER = Number.MIN_SAFE_INTEGER;//没有⾃环const matrix= [[MIN_INTEGER, 9, 2, MAX_INTEGER, 6],[9, MIN_INTEGER, 3, MAX_INTEGER, MAX_INTEGER],[2, 3, MIN_INTEGER, 5, MAX_INTEGER],[MAX_INTEGER, MAX_INTEGER, 5, MIN_INTEGER, 1],[6, MAX_INTEGER, MAX_INTEGER, 1, MIN_INTEGER]];这个邻接矩阵表⽰的图如下:三、边的表⽰⼀个边具有权重、起点、重点三个属性,所以可以创建⼀个类(对象),实现如下:/*** 边对象* */function Edge(begin, end, weight) {this.begin = begin;this.end = end;this.weight = weight;}Edge.prototype.getBegin = function () {return this.begin;};Edge.prototype.getEnd = function () {return this.end;};Edge.prototype.getWeight = function () {return this.weight;};/*class Edge {constructor(begin, end, weight) {this.begin = begin;this.end = end;this.weight = weight;}getBegin() {return this.begin;}getEnd() {return this.end;}getWeight() {return this.weight;}}*/PS:JS这门语⾔没有私有变量的说法,这⾥写get⽅法纯粹是模拟⼀下私有变量。
java实现最⼩⽣成树的prim算法和kruskal算法在边赋权图中,权值总和最⼩的⽣成树称为最⼩⽣成树。
构造最⼩⽣成树有两种算法,分别是prim算法和kruskal算法。
在边赋权图中,如下图所⽰:在上述赋权图中,可以看到图的顶点编号和顶点之间邻接边的权值,若要以上图来构建最⼩⽣成树。
结果应该如下所⽰:这样构建的最⼩⽣成树的权值总和最⼩,为17在构建最⼩⽣成树中,⼀般有两种算法,prim算法和kruskal算法在prim算法中,通过加⼊最⼩邻接边的⽅法来建⽴最⼩⽣成树算法。
⾸先构造⼀个零图,在选⼀个初始顶点加⼊到新集合中,然后分别在原先的顶点集合中抽取⼀个顶点,使得构成的边为权值最⼩,然后将该笔边加⼊到图中,并将抽出的顶点加⼊到新集合中,重复这个过程,知道新集合等于原先的集合。
代码⼀:(java)1/**2 * 最⼩⽣成树的prim算法3 * @author liuy4*/5public class Prim {67public static void prim(int num, float[][] weight) { //num为顶点数,weight为权8float[] lowcost = new float[num + 1]; //到新集合的最⼩权910int[] closest = new int[num + 1]; //代表与s集合相连的最⼩权边的点1112boolean[] s = new boolean[num + 1]; //s[i] == true代表i点在s集合中1314 s[1] = true; //将第⼀个点放⼊s集合1516for(int i = 2; i <= num; i++) { //初始化辅助数组17 lowcost[i] = weight[1][i];18 closest[i] = 1;19 s[i] = false;20 }2122for(int i = 1; i < num; i++) {23float min = Float.MAX_VALUE;24int j = 1;25for(int k = 2; k <= num; k++) {26if((lowcost[k] < min) && (!s[k])) {//根据最⼩权加⼊新点27 min = lowcost[k];28 j = k;29 }30 }3132 System.out.println("加⼊点" + j + ". " + j + "---" + closest[j]);//新加⼊点的j和与j相连的点3334 s[j] = true;//加⼊新点j3536for(int k = 2; k <= num; k++) {37if((weight[j][k] < lowcost[k]) && !s[k]) {//根据新加⼊的点j,求得最⼩权38 lowcost[k] = weight[j][k];39 closest[k] = j;40 }41 }42 }43 }4445public static void main(String[] args) {46// ①47// / | /48// 6 1 549// / | /50// ②-5--③--5--④51// / // /52// 3 6 4 253//////54// ⑤--6-⑥55//最⼩⽣成树为:56// ①57// |58// 159// |60// ②-5--③④61// / / /62// 3 4 263// / //64// ⑤⑥65//66float m = Float.MAX_VALUE;67float[][] weight = {{0, 0, 0, 0, 0, 0, 0},68 {0, m, 6, 1, 5, m, m},69 {0, 6, m, 5, m, 3, m},70 {0, 1, 5, m, 5, 6, 4},71 {0, 5, m, 5, m, m, 2},72 {0, m, 3, 6, m, m, 6},73 {0, m, m, 4, 2, 6, m}};//上图的矩阵74 prim(weight.length - 1, weight);75//加⼊点3. 3---176//加⼊点6. 6---377//加⼊点4. 4---678//加⼊点2. 2---379//加⼊点5. 5---280 }81 }View Code代码⼆:(java)1package最⼩⽣成树;2/*3 * 最⼩⽣成树prim算法,加⼊最⼩邻接边⽣成最⼩⽣成树。
最小生成树的两种算法包括:
1. Prim算法:
Prim算法是一种选择点加入树的算法。
首先选择任意一点作为树的第一个节点,然后枚举与它相连的所有点,将两点之间的边权记为这个点到生成树的距离,选择距离最近的点加入生成树,然后枚举与之相邻的节点,用边权更新该节点的距离,使距离等于两个节点之间的边的权重和。
再继续加入当前离生成树最近的点,在更新它相邻的点,以此类推,直到所有点全部加入生成树。
这样就求出了最小生成树。
2. Kruskal算法:
Kruskal算法也称为“加边法”。
首先把图中的所有边按代价从小到大排序,把图中的n个顶点看成独立的n棵树组成的森林,按权值从小到大选择边,所选的边连接的两个顶点应该属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。
重复以上步骤,直到所有顶点都在一颗树内或者有n-1条边为止。
这样就可以得到最小生成树。
以上信息仅供参考,可以咨询计算机专业人士或者查看专业书籍,
以获取更准确更全面的内容。
一、概述在图论中,prim算法和kruskal算法是两种常用的最小生成树算法。
它们分别以不同的方式来寻找给定图的最小生成树,是解决最小生成树问题的有效方法。
本文将重点介绍prim算法和kruskal算法,并通过例题分析,展示它们的应用及原理。
二、prim算法1. prim算法概述2. prim算法步骤3. 例题分析:通过一个具体图示例,展示prim算法的应用过程,详细阐述每一步的操作及思路。
4. prim算法优缺点三、kruskal算法1. kruskal算法概述2. kruskal算法步骤3. 例题分析:通过一个具体图示例,展示kruskal算法的应用过程,详细阐述每一步的操作及思路。
4. kruskal算法优缺点四、prim算法和kruskal算法的比较1. 时间复杂度2. 空间复杂度3. 适用范围4. 其他特点五、例题分析总结通过对两种算法在具体例题中的应用过程分析,总结prim算法和kruskal算法的异同点,以及在实际问题中应用时的考虑因素。
六、结论根据对prim算法和kruskal算法的介绍及例题分析,总结两种算法的特点和应用场景,为不同情况下的最小生成树问题提供参考指导。
七、参考文献列出本文所参考的相关书籍、文献或全球信息站信息,为读者进一步了解prim算法和kruskal算法提供便利。
八、附录可放置示例代码、补充说明或其他相关内容,以便读者更好地理解prim算法和kruskal算法。
由于当前训练模型对于编程题的掌握有一定限制,可以提供在Prim算法和Kruskal算法方面相关的例题解析和应用案例。
以下是一个基于图的例题及其解析。
假设有一个带权重的无向连通图G,图中的顶点集合为V,边的集合为E,每条边的权重由权重函数w(u, v)给出,其中u, v为边的两个顶点。
现在需要使用Prim算法和Kruskal算法来寻找图G的最小生成树。
首先我们需要给出一个具体的图G,如下所示:顶点集合V = {A, B, C, D, E}边的集合E = {(A, B, 3), (A, C, 1), (A, D, 5), (B, C, 4), (B, D, 6), (B, E, 2), (C, D, 7), (C, E, 8), (D, E, 9)}其中,每个元组表示一条边的起始顶点、终止顶点和权重。
/*分别利用prim算法和kruskal算法实现求图的最小生成树*/ #include<stdio.h>#include<stdlib.h>#define MaxVertexNum 12#define MaxEdgeNum 20#define MaxValue 1000typedef int Vertextype;typedef int adjmatrix[MaxVertexNum][MaxVertexNum]; typedef Vertextype vexlist[MaxVertexNum];int visited[MaxVertexNum]={0};struct edgeElem{int fromvex;int endvex;int weight;};typedef struct edgeElem edgeset[MaxVertexNum];void Creat_adjmatrix(vexlist GV,adjmatrix GA,int n,int e) {int i,j,k,w;printf("输入%d个顶点数据",n);for(i=0;i<n;i++)scanf("%d",&GV[i]);for(i=0;i<n;i++)for(j=0;j<n;j++)if(i==j) GA[i][j]=0;else GA[i][j]=MaxValue;printf("输入%d条无向带权边",e);for(k=0;k<e;k++){scanf("%d%d%d",&i,&j,&w);GA[i][j]=GA[j][i]=w;}}void Creat_edgeset(vexlist GV,edgeset GE,int n,int e) {int i,j,k,w;printf("输入%d个顶点数据",n);for(i=0;i<n;i++)scanf("%d",&GV[i]);printf("输入%d条无向带权边",e);for(k=0;k<e;k++){ scanf("%d%d%d",&i,&j,&w);GE[k].fromvex=i;GE[k].endvex=j;GE[k].weight=w;}}void output_edgeset(edgeset GE,int e){int k;for(k=0;k<e;k++)printf("%d %d %d,",GE[k].fromvex,GE[k].endvex,GE[k].weight); printf("\n");}void prim(adjmatrix GA,edgeset CT,int a,int n){int i,j,t,k,w,min,m;struct edgeElem x;for(i=0;i<n;i++)if(i<a){CT[i].fromvex=a;CT[i].endvex=i;CT[i].weight=GA[a][i];}else if(i>a){CT[i-1].fromvex=a;CT[i-1].endvex=i;CT[i-1].weight=GA[a][i];}for(k=1;k<n;k++){min=MaxValue;m=k-1;for(j=k-1;j<n-1;j++)if(CT[j].weight<min){min=CT[j].weight;m=j;}x=CT[k-1];CT[k-1]=CT[m];CT[m]=x;j=CT[k-1].endvex;for(i=k;i<n-1;i++){t=CT[i].endvex;w=GA[j][t];if(w<CT[i].weight){CT[i].weight=w;CT[i].fromvex=j;}}}}void kruskal(edgeset GE,edgeset C,int n){ int i,j,k,d;int m1,m2;adjmatrix s;for(i=0;i<n;i++){for(j=0;j<n;j++)if(i==j) s[i][j]=1;else s[i][j]=0;}k=1;d=0;while(k<n){for(i=0;i<n;i++){if(s[i][GE[d].fromvex]==1) m1=i;if(s[i][GE[d].endvex]==1) m2=i;}if(m1!=m2){C[k-1]=GE[d];k++;for(j=0;j<n;j++){s[m1][j]=s[m1][j]||s[m2][j];s[m2][j]=0;}}d++;}}void main(){int n,e;vexlist GV;adjmatrix GA;edgeset GE,C;printf("输入图的顶点数和边数:");scanf("%d%d",&n,&e);Creat_adjmatrix( GV, GA, n, e);printf("利用prim算法从0点出发求图的最小生成树:\n");prim(GA,GE,0,n);output_edgeset( GE, n-1);printf("输入图的顶点数和边数:");scanf("%d%d",&n,&e);Creat_edgeset( GV,GE,n, e);printf("利用kruskal算法从0点出发求图的最小生成树:\n");kruskal( GE, C, n);output_edgeset( C, n-1);}最小生成树(prim算法和kruskal算法)收藏最小生成树(prim算法)用最小生成树的解决的经典问题:若要在n个城市间建设通信网路,给出任意两个城市的距离和每米通信网路的造价,问怎样设计网络可以使网路的造价最小。
用prim算法和Kruskal算法求最小生成树2013-10-15 星期二一、问题表述分别用prim算法和Kruskal算法求下面图的最小生成树二、实验过程与结果(含程序代码)解:在C++中输入prim算法#include <stdio.h>#include <limits.h>#define N 100int p[N], key[N], tb[N][N];void prim(int v, int n){int i, j;int min;for (i = 1; i <= n; i++){p[i] = v;key[i] = tb[v][i];}key[v] = 0;for (i = 2; i <= n; i++){min = INT_MAX;for (j = 1; j <= n; j++)if (key[j] > 0 && key[j] < min){v = j;min = key[j];}printf("%d%d ", p[v], v);key[v] = 0;for (j = 1; j <= n; j++)if (tb[v][j] < key[j])p[j] = v, key[j] = tb[v][j];}}int main(){int n, m;int i, j;int u, v, w;printf("请输入节点个数n和边数m\n");//scanf("%d %d", &n, &m);while (scanf("%d%d", &n, &m)){for(i = 1; i <= n; i++){for (j = 1; j <= n; j++)tb[i][j] = INT_MAX;}while (m--){ printf("请输入一边以及权值\n");scanf("%d%d%d", &u, &v, &w);tb[u][v] = tb[v][u] = w;}printf("输出边路径:");prim(1, n);printf("\n");}return 0;}Kruskal算法:#include"malloc.h"#include"stdlib.h"#include"stdio.h"#include <io.h>#include <windows.h>#include <limits.h>#define MAX_VERTEX_NUM 20 // 最大顶点个数#define MAX_NAME 3 // 顶点字符串的最大长度+1#define MAX_INFO 20 // 相关信息字符串的最大长度+1 #define INFINITY INT_MAX // 用整型最大值代替∞typedef int VRType;typedef char InfoType;typedef char VertexType[MAX_NAME];// 邻接矩阵的数据结构typedef structVRType adj; // 顶点关系类型。
/*分别利用prim算法和kruskal算法实现求图的最小生成树*/ #include<stdio.h>#include<stdlib.h>#define MaxVertexNum 12#define MaxEdgeNum 20#define MaxValue 1000typedef int Vertextype;typedef int adjmatrix[MaxVertexNum][MaxVertexNum]; typedef Vertextype vexlist[MaxVertexNum];int visited[MaxVertexNum]={0};struct edgeElem{int fromvex;int endvex;int weight;};typedef struct edgeElem edgeset[MaxVertexNum];void Creat_adjmatrix(vexlist GV,adjmatrix GA,int n,int e) {int i,j,k,w;printf("输入%d个顶点数据",n);for(i=0;i<n;i++)scanf("%d",&GV[i]);for(i=0;i<n;i++)for(j=0;j<n;j++)if(i==j) GA[i][j]=0;else GA[i][j]=MaxValue;printf("输入%d条无向带权边",e);for(k=0;k<e;k++){scanf("%d%d%d",&i,&j,&w);GA[i][j]=GA[j][i]=w;}}void Creat_edgeset(vexlist GV,edgeset GE,int n,int e) {int i,j,k,w;printf("输入%d个顶点数据",n);for(i=0;i<n;i++)scanf("%d",&GV[i]);printf("输入%d条无向带权边",e);for(k=0;k<e;k++){ scanf("%d%d%d",&i,&j,&w);GE[k].fromvex=i;GE[k].endvex=j;GE[k].weight=w;}}void output_edgeset(edgeset GE,int e){int k;for(k=0;k<e;k++)printf("%d %d %d,",GE[k].fromvex,GE[k].endvex,GE[k].weight); printf("\n");}void prim(adjmatrix GA,edgeset CT,int a,int n){int i,j,t,k,w,min,m;struct edgeElem x;for(i=0;i<n;i++)if(i<a){CT[i].fromvex=a;CT[i].endvex=i;CT[i].weight=GA[a][i];}else if(i>a){CT[i-1].fromvex=a;CT[i-1].endvex=i;CT[i-1].weight=GA[a][i];}for(k=1;k<n;k++){min=MaxValue;m=k-1;for(j=k-1;j<n-1;j++)if(CT[j].weight<min){min=CT[j].weight;m=j;}x=CT[k-1];CT[k-1]=CT[m];CT[m]=x;j=CT[k-1].endvex;for(i=k;i<n-1;i++){t=CT[i].endvex;w=GA[j][t];if(w<CT[i].weight){CT[i].weight=w;CT[i].fromvex=j;}}}}void kruskal(edgeset GE,edgeset C,int n){ int i,j,k,d;int m1,m2;adjmatrix s;for(i=0;i<n;i++){for(j=0;j<n;j++)if(i==j) s[i][j]=1;else s[i][j]=0;}k=1;d=0;while(k<n){for(i=0;i<n;i++){if(s[i][GE[d].fromvex]==1) m1=i;if(s[i][GE[d].endvex]==1) m2=i;}if(m1!=m2){C[k-1]=GE[d];k++;for(j=0;j<n;j++){s[m1][j]=s[m1][j]||s[m2][j];s[m2][j]=0;}}d++;}}void main(){int n,e;vexlist GV;adjmatrix GA;edgeset GE,C;printf("输入图的顶点数和边数:");scanf("%d%d",&n,&e);Creat_adjmatrix( GV, GA, n, e);printf("利用prim算法从0点出发求图的最小生成树:\n");prim(GA,GE,0,n);output_edgeset( GE, n-1);printf("输入图的顶点数和边数:");scanf("%d%d",&n,&e);Creat_edgeset( GV,GE,n, e);printf("利用kruskal算法从0点出发求图的最小生成树:\n");kruskal( GE, C, n);output_edgeset( C, n-1);}最小生成树(prim算法和kruskal算法)收藏最小生成树(prim算法)用最小生成树的解决的经典问题:若要在n个城市间建设通信网路,给出任意两个城市的距离和每米通信网路的造价,问怎样设计网络可以使网路的造价最小。
在n个点中间最多可以有n(n-1)/2条无向边存在,最多可以有n(n-1)个有向边存在。
只要n-1边就可以将n个点连接成一个生成树。
Prim算法:自己感觉很像缔结斯特拉算法。
步骤:1. 随便找一个点,设置为已使用。
2. 找已使用点和未使用点之间所有连线中最短的一条,然后将这条连线中未使用的点列入已使用。
3. 如此反复1,2直到说有的点变成已使用点。
//map中存储着全图,n(n+1)条线的情况.num表示点的个数,treemap是生成的树void prim(int map[][N],int num,int treemap[][N])//treemap[][]初始化为0...{bool *used=new bool[num];//用来判断该点是否已经用过int count=num;//用来计数int point=0;//用来代表即将加入的点的上一个点int willuse=0;//用来代表即将加入的点int mindage;//用来代表当前的最小路程//初始化used[]used[0]=true;for(int m=1;m<num;m++)used[m]=false;while(count--)...{mindage=9999999;for(int i=0;i<num;i++)...{for(int j=0;j<num;j++)...{if(used[j])//find a used point...{for(int k=0;k<num;k++)...{if(!used[k]&&map[j][k]<mindage)//find unused point...{point=j;willuse=k;mindage=map[j][k];}}}}}treemap[point][willuse]=treemap[willuse][point]=1;//将point到willuse这两个点联通used[willuse]=1;//将willuse加入已用集合.}}kruskal算法:步骤:1. 将每一个点看成一个连通分量,在连通分量间找最短的边,然后连到一起,那么在图中就有n-1个连通分量了。
2. 在现在的连通分量的情况下找任意两个连通分量间最短的边中最短的边,将这两个连通分量连到一起,这样往复知道所有的点在同一个联通分量中就可得到最小生成树。
代码如下:void kruskal(int map[][N],int num,int treemap[][N])//treemap初始化为0...{int *flag=new int[num];//用来判断连个点是不是在同一个连通分量里int count=num-1;//用来为边计数要n-1条边int mindis,number1,number2,from,to;//中间变量for(int i=0;i<num;i++)flag[i]=i+1;//将每个点的划分成一个连通分量while(count--)...{mindis=99999999;//用来记录当前最小的边for(int j=0;j<num;j++)...{for(int g=0;g<num;g++)...{if(j==g)continue;//当两个点不在同一个连通分量中并且他们的边比较小时.else if(flag[j]!=flag[g]&&map[j][g]<mindis)...{mindis=map[j][g];number1=(flag[j]>flag[g]?flag[j]:flag[g]);number2=(flag[g]>flag[j]?flag[j]:flag[g]);from=j;to=g;}}}treemap[from][to]=treemap[to][from]=1; //为1即连通for(int m=0;m<num;m++)...{if(flag[m]==number2)flag[m]=number1;}}}测试用语句:#include <iostream>using namespace std;#define N 6//语句插入位子.int main()...{int map[6][6]=...{...{100,6,1,5,100,100},...{6,100,5,100,3,100},...{1,5,100,5,6,4},...{5,100,5,100,100,2},...{100,3,6,100,100,6},...{100,100,4,2,6,100}};int treemap[6][6];//函数调用位子system("pause");return 0;}这代码自己写的感觉上不是很顺,有时间再改一下。