Kruskal 算法
库鲁斯卡尔(Kruskal)算法
贪心策略是指从问题的初始状态出发,通过若干次的贪心选择而得出最优值(或较优解)的一种解题方法。
Ⅰ、库鲁斯卡尔(Kruskal)算法【定义4】设图G=(V,E)是一简单连通图,|V| =n,|E|=m,每条边ei都给以权W ,W 假定是边e 的长度(其他的也可以),i=1,2,3,...,m。
求图G的总长度最短的树,这就是最短树问题。
kruskal算法的基本思想是:首先将赋权图G的边按权的升序排列,不失一般性为:e ,e ,......,e 。
其中W ≤W ,然后在不构成回路的条件下择优取进权最小的边。
其流程如下:(1)对属于E的边进行排序得e ≤e ≤...... ≤e 。
(2)初始化操作 w←0,T←ф,k←0,t←0;(3)若t=n-1,则转(6),否则转(4)(4)若T∪{e }构成一回路,则作【k←k+1,转(4)】(5) T←T∪{ e },w←w+ w ,t←t+1,k←k+1,转(3)(6)输出T,w,停止。
下面我们对这个算法的合理性进行证明。
设在最短树中,有边〈v ,v 〉,连接两顶点v ,v ,边〈v ,v 〉的权为wp,若〈v ,v 〉加入到树中不能保证树的总长度最短,那么一定有另一条边〈v ,v 〉或另两条边〈v ,v 〉、〈v ,v 〉,且w<vi,vj><wp或w<vi,vk>+w〈vk,vj〉<wp,因为〈v ,v 〉、〈v ,v 〉不在最短树中,可知当〈v ,v 〉、〈v ,v 〉加入到树中时已构成回路,此时程序终止。
因为〈v ,v 〉∈ T,〈v ,v 〉∈T且w〈vI,vk〉+w〈vk,vj〉<w p,与程序流程矛盾。
普林(Prim)算法:Kruskal算法采取在不构成回路的条件下,优先选择长度最短的边作为最短树的边,而Prim则是采取了另一种贪心策略。
已知图G=(V,E),V={v ,v ,v ,..., v },D=(d )是图G的矩阵,若〈v ,v 〉∈E,则令dij=∞,并假定dij=∞Prim算法的基本思想是:从某一顶点(设为v )开始,令S←{v },求V/S中点与S中点v 距离最短的点,即从矩阵D的第一行元素中找到最小的元素,设为d ,则令S ←S∪ { v },继续求V/S中点与S的距离最短的点,设为v ,则令S←S∪{ v },继续以上的步骤,直到n个顶点用n-1条边连接起来为止。
kruskal算法
kruskal算法百科名片K r u s k a l算法每次选择n- 1条边,所使用的贪婪准则是:从剩下的边中选择一条不会产生环路的具有最小耗费的边加入已选择的边的集合中。
注意到所选取的边若产生环路则不可能形成一棵生成树。
K r u s k a l算法分e 步,其中e 是网络中边的数目。
按耗费递增的顺序来考虑这e 条边,每次考虑一条边。
当考虑某条边时,若将其加入到已选边的集合中会出现环路,则将其抛弃,否则,将它选入。
目录[隐藏]Kruskal算法普里姆算法(prim算法)Kruskal算法普里姆算法(prim算法)[编辑本段]Kruskal算法算法定义克鲁斯卡尔算法假设WN=(V,{E}) 是一个含有n 个顶点的连通网,则按照克鲁斯卡尔算法构造最小生成树的过程为:先构造一个只含n 个顶点,而边集为空的子图,若将该子图中各个顶点看成是各棵树上的根结点,则它是一个含有n 棵树的一个森林。
之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。
依次类推,直至森林中只有一棵树,也即子图中含有n-1条边为止。
举例描述初始时没有任何边被选择。
边( 1 , 6)是最先选入的边,它被加入到欲构建的生成树中,得到图1 3 - 1 2 c。
下一步选择边(3,4)并将其加入树中(如图1 3 - 1 2 d所示)。
然后考虑边( 2,7 ,将它加入树中并不会产生环路,于是便得到图1 3 - 1 2 e。
下一步考虑边(2,3)并将其加入树中(如图1 3 - 1 2 f所示)。
在其余还未考虑的边中,(7,4)具有最小耗费,因此先考虑它,将它加入正在创建的树中会产生环路,所以将其丢弃。
此后将边(5,4)加入树中,得到的树如图13-12g 所示。
下一步考虑边(7,5),由于会产生环路,将其丢弃。
② kruskal最小生成树算法的定义和实现
Krskal最小生成树算法是一种用来解决图论中最小生成树问题的算法。
它采用了一种贪心的策略,即每一步都选择权值最小的边,并且保证选择的边不会构成环,直到生成一棵最小生成树为止。
1. 算法的定义kruskal最小生成树算法的定义如下:输入:一个连通无向图G=(V,E),其中V是顶点集合,E是边的集合,每条边都有一个权值。
输出:G的最小生成树算法步骤:① 将图G的所有边按照权值从小到大进行排序。
② 初始化一个空的边集合T,该集合用来存放最小生成树的边。
③ 依次遍历排序后的边集合,对于每一条边e=(u,v),如果将该边添加到T中不会构成环,则将其添加到T中。
④ 重复步骤③,直到T中的边数等于V-1为止,此时T就是G的最小生成树。
2. 算法的实现下面是kruskal最小生成树算法的具体实现过程:```pythonclass UnionFind:def __init__(self, n):self.parent = [i for i in range(n)]def find(self, x):if self.parent[x] != x:self.parent[x] = self.find(self.parent[x]) return self.parent[x]def union(self, x, y):root_x = self.find(x)root_y = self.find(y)if root_x != root_y:self.parent[root_x] = root_ydef kruskal(graph):edges = []for u in range(len(graph)):for v in range(u+1, len(graph)):if graph[u][v] != 0:edges.append((u, v, graph[u][v])) edges.sort(key=lambda x: x[2])n = len(graph)result = []uf = UnionFind(n)for edge in edges:u, v, weight = edgeif uf.find(u) != uf.find(v):uf.union(u, v)result.append((u, v, weight))if len(result) == n - 1:breakreturn result```以上代码实现了kruskal最小生成树算法,其中使用了UnionFind类来实现并查集的功能,以判断是否构成环。
kruskal函数的各个参数
Kruskal函数是一种用于解决最小生成树问题的算法。
它通常用于在图论中寻找连接所有节点的最小数量的边。
Kruskal函数通常需要以下参数:1. 输入数据:输入数据通常是一个图的数据结构,包括节点的集合和边的集合。
节点集合表示图中的节点,边集合表示节点之间的连接关系。
2. 节点集合:输入数据中的节点集合表示图中的节点,通常是一个整数数组或列表。
3. 边集合:输入数据中的边集合表示节点之间的连接关系,通常是一个包含边的列表或数组。
每个边由两个节点和一个权重组成,表示从第一个节点到第二个节点的边的权重。
4. 输出结果:Kruskal函数将返回一个最小生成树的结果,通常是一个包含所有边和权重的列表或数组。
以下是Kruskal函数各个参数的详细说明:* 输入数据:输入数据是图的数据结构,包括节点集合和边集合。
节点集合表示图中的节点,边集合表示节点之间的连接关系。
输入数据应该已经经过了适当的处理,例如去除重复的边和检查边的权重是否满足连通性要求等。
* 节点集合:节点集合是一个整数数组或列表,表示图中的节点。
在最小生成树问题中,每个节点都应该至少被包含在生成树中一次。
因此,节点集合应该只包含那些至少被一个边连接的节点。
* 边集合:边集合是一个包含边的列表或数组,表示节点之间的连接关系。
边的权重通常表示了连接的强度或距离。
在最小生成树问题中,边的权重应该被视为非负数,并且权重较小的边应该被优先选择。
Kruskal函数的核心思想是通过将边按照权重从小到大排序,并按照顺序依次选择边来构建生成树。
在选择每一条边时,需要检查它是否已经存在于生成树中,以及它是否会形成环路。
如果满足这两个条件,则可以选择这条边并将其添加到生成树中。
通过这种方式,Kruskal 函数可以逐步构建最小生成树,直到所有节点都被连接为止。
总之,Kruskal函数的各个参数包括输入数据、节点集合和边集合,以及输出结果。
输入数据应该是一个经过适当处理的数据结构,其中包含节点和边的信息。
Kruskal算法
1. Kruskal算法(1) 算法思想K r u s k a l算法每次选择n- 1条边,所使用的贪婪准则是:从剩下的边中选择一条不会产生环路的具有最小耗费的边加入已选择的边的集合中。
注意到所选取的边若产生环路则不可能形成一棵生成树。
K r u s k a l算法分e 步,其中e 是网络中边的数目。
按耗费递增的顺序来考虑这e 条边,每次考虑一条边。
当考虑某条边时,若将其加入到已选边的集合中会出现环路,则将其抛弃,否则,将它选入。
初始时没有任何边被选择。
边( 1 , 6)是最先选入的边,它被加入到欲构建的生成树中,得到图1 3 - 1 2 c。
下一步选择边( 3,4)并将其加入树中(如图1 3 - 1 2 d所示)。
然后考虑边( 2,7 ,将它加入树中并不会产生环路,于是便得到图1 3 - 1 2 e。
下一步考虑边( 2,3)并将其加入树中(如图1 3 - 1 2 f所示)。
在其余还未考虑的边中,(7,4)具有最小耗费,因此先考虑它,将它加入正在创建的树中会产生环路,所以将其丢弃。
此后将边( 5,4)加入树中,得到的树如图13-12g 所示。
下一步考虑边( 7,5),由于会产生环路,将其丢弃。
最后考虑边( 6,5)并将其加入树中,产生了一棵生成树,其耗费为9 9。
图1 - 1 3给出了K r u s k a l算法的伪代码。
(2)C代码/* Kruskal.cCopyright (c) 2002, 2006 by ctu_85All Rights Reserved.*//* I am sorry to say that the situation of unconnected graph is not concerned */#include "stdio.h"#define maxver 10#define maxright 100int G[maxver][maxver],record=0,touched[maxver][maxver];int circle=0;int FindCircle(int,int,int,int);int main(){int path[maxver][2],used[maxver][maxver];int i,j,k,t,min=maxright,exsit=0;int v1,v2,num,temp,status=0;restart:printf("Please enter the number of vertex(s) in the graph:\n"); scanf("%d",&num);if(num>maxver||num<0){printf("Error!Please reinput!\n");goto restart;}for(j=0;j<num;j++)for(k=0;k<num;k++){if(j==k){G[j][k]=maxright;used[j][k]=1;touched[j][k]=0;}elseif(j<k){re:printf("Please input the right between vertex %d and vertex %d,if no edge exists please input -1:\n",j+1,k+1);scanf("%d",&temp);if(temp>=maxright||temp<-1){printf("Invalid input!\n");goto re;}if(temp==-1)temp=maxright;G[j][k]=G[k][j]=temp;used[j][k]=used[k][j]=0;touched[j][k]=touched[k][j]=0;}}for(j=0;j<num;j++){path[j][0]=0;path[j][1]=0;}for(j=0;j<num;j++){status=0;for(k=0;k<num;k++)if(G[j][k]<maxright){status=1;break;}if(status==0)break;}for(i=0;i<num-1&&status;i++){for(j=0;j<num;j++)for(k=0;k<num;k++)if(G[j][k]<min&&!used[j][k]){v1=j;v2=k;min=G[j][k];}if(!used[v1][v2]){used[v1][v2]=1;used[v2][v1]=1;touched[v1][v2]=1;touched[v2][v1]=1;path[0]=v1;path[1]=v2;for(t=0;t<record;t++)FindCircle(path[t][0],path[t][0],num,path[t][0]);if(circle){/*if a circle exsits,roll back*/circle=0;i--;exsit=0;touched[v1][v2]=0;touched[v2][v1]=0;min=maxright;}else{record++;min=maxright;}}}if(!status)printf("We cannot deal with it because the graph is not connected!\n"); else{for(i=0;i<num-1;i++)printf("Path %d:vertex %d to vertex %d\n",i+1,path[0]+1,path[1]+1); }return 1;}int FindCircle(int start,int begin,int times,int pre){ /* to judge whether a circle is produced*/int i;for(i=0;i<times;i++)if(touched[begin]==1){if(i==start&&pre!=start){circle=1;return 1;break;}elseif(pre!=i)FindCircle(start,i,times,begin);elsecontinue;}return 1;}。
库鲁斯卡尔(Kruskal)算法
贪心策略是指从问题的初始状态出发,通过若干次的贪心选择而得出最优值(或较优解)的一种解题方法。
Ⅰ、库鲁斯卡尔(Kruskal)算法【定义4】设图G=(V,E)是一简单连通图,|V| =n,|E|=m,每条边ei都给以权W ,W 假定是边e 的长度(其他的也可以),i=1,2,3,...,m。
求图G的总长度最短的树,这就是最短树问题。
kruskal算法的基本思想是:首先将赋权图G的边按权的升序排列,不失一般性为:e ,e ,......,e 。
其中W ≤W ,然后在不构成回路的条件下择优取进权最小的边。
其流程如下:(1)对属于E的边进行排序得e ≤e ≤...... ≤e 。
(2)初始化操作 w←0,T←ф,k←0,t←0;(3)若t=n-1,则转(6),否则转(4)(4)若T∪{e }构成一回路,则作【k←k+1,转(4)】(5) T←T∪{ e },w←w+ w ,t←t+1,k←k+1,转(3)(6)输出T,w,停止。
下面我们对这个算法的合理性进行证明。
设在最短树中,有边〈v ,v 〉,连接两顶点v ,v ,边〈v ,v 〉的权为wp,若〈v ,v 〉加入到树中不能保证树的总长度最短,那么一定有另一条边〈v ,v 〉或另两条边〈v ,v 〉、〈v ,v 〉,且w<vi,vj><wp或w<vi,vk>+w〈vk,vj〉<wp,因为〈v ,v 〉、〈v ,v 〉不在最短树中,可知当〈v ,v 〉、〈v ,v 〉加入到树中时已构成回路,此时程序终止。
因为〈v ,v 〉∈ T,〈v ,v 〉∈T且w〈vI,vk〉+w〈vk,vj〉<w p,与程序流程矛盾。
普林(Prim)算法:Kruskal算法采取在不构成回路的条件下,优先选择长度最短的边作为最短树的边,而Prim则是采取了另一种贪心策略。
已知图G=(V,E),V={v ,v ,v ,..., v },D=(d )是图G的矩阵,若〈v ,v 〉∈E,则令dij=∞,并假定dij=∞Prim算法的基本思想是:从某一顶点(设为v )开始,令S←{v },求V/S中点与S中点v 距离最短的点,即从矩阵D的第一行元素中找到最小的元素,设为d ,则令S ←S∪ { v },继续求V/S中点与S的距离最短的点,设为v ,则令S←S∪{ v },继续以上的步骤,直到n个顶点用n-1条边连接起来为止。
最小二叉树算法
最小二叉树算法
最小二叉树算法(Minimum Spanning Tree,MST)是一种用于解决带权连通图的优化问题的算法。
它可以找到一棵包含所有节点的生成树,使得生成树上所有边的权值之和最小。
最小二叉树算法的基本思路如下:
1. 选择一个起点,将其加入生成树中。
2. 在未加入生成树的边中,选择一条与生成树中的节点相连的权值最小的边,将其加入生成树中。
3. 重复步骤2,直到所有节点都被加入生成树中,此时生成树就是最小生成树。
最小二叉树算法有两种常用的实现方法:Prim算法和Kruskal算法。
Prim算法的实现步骤如下:
1. 选择一个起点,将其加入生成树中。
2. 在未加入生成树的边中,选择一条与生成树中的节点相连的权值最小的
边,将其加入生成树中。
3. 将新加入的节点也加入生成树中,重复步骤2和3,直到所有节点都被加入生成树中,此时生成树就是最小生成树。
Kruskal算法的实现步骤如下:
1. 将所有边按照权值从小到大排序。
2. 依次选择每条边,如果这条边的两个节点不在同一个连通块中,则将这条边加入生成树中,并将这两个节点合并为一个连通块。
3. 重复步骤2,直到所有节点都在同一个连通块中,此时生成树就是最小生成树。
最小二叉树算法在网络设计、电路设计、物流运输等领域都有广泛应用,能够有效地优化问题,提高效率和效益。
kruskal算法回环判断方法
kruskal算法回环判断方法(原创实用版4篇)《kruskal算法回环判断方法》篇1Kruskal 算法是一种用于寻找最小生成树的算法。
在Kruskal 算法中,边按照权重从小到大排序,然后依次将边加入到图中,但要避免形成环。
为了判断是否形成环,Kruskal 算法使用了一种称为“回环判断方法”的技术。
具体来说,在加入一条边之前,需要检查这条边是否与已加入的边形成环。
如果形成环,则这条边不能加入到图中。
回环判断方法的实现可以通过使用并查集数据结构来实现。
具体来说,对于每一条边,都使用一个并查集来记录这条边所连接的顶点属于哪个连通分量。
在加入一条边之前,需要检查这条边的两个端点是否属于同一个连通分量。
如果属于同一个连通分量,则说明加入这条边会形成环,不能加入。
《kruskal算法回环判断方法》篇2Kruskal 算法是一种用于寻找最小生成树的算法。
在寻找最小生成树时,需要判断一个树是否是一个回环。
回环是指一个节点通过一条边连接到自己,形成一个环。
Kruskal 算法使用并查集数据结构来维护边集,并使用disjoint sets data structure 来判断是否存在回环。
在disjoint sets data structure 中,每个节点代表一个连通分量(也可以理解为森林中的一个组成部分),每个节点的父节点是指向它的连通分量的根节点。
当加入一条新边时,需要将这条边的两个端点的节点合并到同一个连通分量中。
如果这条边的两个端点已经在同一个连通分量中,那么就说明存在回环。
具体实现时,可以使用一个数组来记录每个节点的父节点,当加入一条新边时,需要遍历这条边的两个端点的父节点,如果它们相同,就说明存在回环。
以下是一个示例代码:``` pythonclass DisjointSet:def __init__(self):self.size = 0self.parent = [None] * (100000 + 1)def find(self, x):if self.parent[x] is None:return xelse:return self.find(self.parent[x])def union(self, x, y):x_root = self.find(x)y_root = self.find(y)if x_root == y_root:#存在回环returnelse:if self.size[x_root] < self.size[y_root]:self.size[x_root] += self.size[y_root]self.parent[x_root] = x_rootelse:self.size[y_root] += self.size[x_root]self.parent[y_root] = x_root```在以上代码中,`self.size[i]` 表示连通分量i 的大小,`self.parent[i]` 表示连通分量i 的根节点。
求无向图的最小生成树算法——Prim与Kruskal
1.算法思想
对于图G=(V,E),用Prim算法求最小生成树T=(S,TE)的流程如下
① 初始化:设S、TE为空集,任选节点K加入S。
② 选取一条权值最小的边(X,Y),其中X∈S,且not (Y∈S) 即,选取一条权值最小的、连接着S中一点与S外一点的边。
以上操作重复|V|-1次结束。由于每次加入S的点i都在当时取到了符合流程②的边min{lowcost},而lowcost[i]=w(i,closest[i]),所以此时的最小生成树的各边就是(i,closest[i]),i∈V且not (i=x)【需要注意的是出发点x的closest[x]还是x,所以应忽略,实际取到x-1条边】。把i从1取到|V|,便得到最小生成树T的每条边。
为了比较快速地选边,我们用两个数组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。
lowcost[j] = w[k][j];
closest[j] = k;
} //由新加入S中的k点使某些点到S的距离缩短,所以更新各点的lowcost和close= 1; i <= n; i++)
if(i != closest[i]){
设出发点为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】
Kruskal算法
1. 概览Kruskal算法是一种用来寻找最小生成树的算法,由Joseph Kruskal在1956年发表。
用来解决同样问题的还有Prime 算法和Boruvka 算法等。
三种算法都是贪婪算法的应用。
和Boruvka 算法不同的地方是,Kruskal 算法在图中存在相同权值的边时也有效。
2. 算法简单描述1.记Graph 中有v个顶点,e个边2.新建图Graph new,Graph new中拥有原图中相同的e个顶点,但没有边3.将原图Graph new中所有e个边按权值从小到大排序4.循环:从权值最小的边开始遍历每条边直至图Graph new中所有的节点都在同一个连通分量中如果这条边连接的两个节点于图Graph new中不在同一个连通分量中添加这条边到图Graph new中图例描述:3. 简单证明Kruskal算法对图的顶点数n 做归纳,证明Kruskal 算法对任意n 阶图适用。
归纳基础:n = 1,显然能够找到最小生成树。
归纳过程:假设Kruskal 算法对n ≤k 阶图适用,那么,在k + 1 阶图G 中,我们把最短边的两个端点a 和b 做一个合并操作,即把u 与v 合为一个点v',把原来接在u 和v 的边都接到v' 上去,这样就能够得到一个k阶图G'(u ,v 的合并是k + 1 少一条边),G' 最小生成树T' 可以用Kruskal 算法得到。
我们证明T' + {<u,v>} 是G 的最小生成树。
用反证法,如果T' + {<u,v>} 不是最小生成树,最小生成树是T,即W(T) < W(T' + {<u,v>})。
显然T 应该包含<u,v>,否则,可以用<u,v> 加入到T 中,形成一个环,删除环上原有的任意一条边,形成一棵更小权值的生成树。
而T - {<u,v>},是G' 的生成树。
Kruskal算法说明及图解
1.无向网图及边集数组存储示意图vertex[6]=2.Kruskal 方法构造最小生成树的过程(a)一个图 (b)最小生成树过程1V0 V1 V2 V3 V4 V5下标 0 1 2 3 4 5 6 7 8 from 1 2 0 2 3 4 0 3 0 to 4 3 5 5 5 5 1 4 2 weigh t121719252526343846V1V0V4 V5V2 V3V1V0 V5 V2 V3 V4(c)最小生成树过程2 (d)最小生成树过程3(e)最小生成树过程3.伪代码1)初始化辅助数组parent[vertexNum];num=0; 2) 依次考查每一条边for(i=0; i<arcNum; i++ ) vex1=edge[i].form 所在生成树的根结点 vex2=edge[i].to 所在生成树的根结点 If(vex1!=vex2)parent[vex2]=vex1; num++;if(num==vertexNum-1) 算法结束 4.构造过程中参数变化顶点集 数组 parent V0 V1 V2 V3 V4 V5 被考查边 输出说明初始化parent -1 -1 -1 -1 -1 -16棵生成树,均只有根结点parent -1 -1 -1 -1 1 -1 (v1,v4)12 (v1,v4)12 vex1=1,vex2=4;parent[4]=1; parent -1 -1 -1 2 1 -1 (v2,v3)17 (v2,v3)17 vex1=2,vex2=3;parent[3]=2; parent -1 -1 -1 2 1 0 (v0,v5)19 (v0,v5)19 vex1=0,vex2=5;parent[5]=0; parent2-1 -1 21(v2,v5)25(v2,v5)25 vex1=2,vex2=0;parent[0]=2;5.主要代码/**********构造函数************/template<class DataType>EdgeGraph<DataType>::EdgeGraph(DataType a[], int n, int e){vertexNum=n; edgeNum=e;int i,j,weight;for (int k=0; k<vertexNum; k++)vertex[k]=a[k];for (k=0; k<edgeNum; k++){cout<<"输入边依附的两个顶点的编号:";cin>>i>>j;edge[k].from=i;edge[k].to=j;cout<<"请输入权重:";cin>>weight;edge[k].weight=weight;}}/**********Kruskal算法构造最小生成树************/template <class DataType>void EdgeGraph<DataType>::Kruskal(){ int num;int parent[MaxVertex], vex1, vex2;for (int i=0; i<vertexNum; i++)parent[i]=-1;for (i=0,num=0; i<edgeNum; i++){vex1=FindRoot(parent, edge[i].from);vex2=FindRoot(parent, edge[i].to);if (vex1!=vex2){cout << "(" << edge[i].from <<"->"<<edge[i].to << ")" <<"weight: "<< edge[i].weight << endl;parent[vex2]=vex1;num++;if (num==vertexNum-1) return;}}}/**********寻找根节点************/template <class DataType>int EdgeGraph<DataType>::FindRoot(int parent[], int v) {int t=v;while(parent[t] > -1)t=parent[t];return t;}/**********遍历输出************/template <class DataType>void EdgeGraph<DataType>::Print(){for (int i=0; i<edgeNum; i++){cout<<"("<<edge[i].from<<""<<edge[i].to<<")"<<"weight:"<<edge[i].weight<<endl;;}}6.结果截图发现不按大小输入weight的值则不能正确生成最小生成树如排好序输入时则得到正确的最小生成树结果正确,所以需要按大小顺序输入权值大小的Kruskal算法实际上对较复杂无向网图来说不适用。
数学建模最小生成树例题
数学建模最小生成树例题例题1:某城市计划建设一条高速公路,需要在若干个村庄之间选择一条最优路径。
已知各个村庄之间的距离,请使用最小生成树算法为高速公路选择最优路径。
参考答案:最小生成树算法可以用于解决此类问题。
常用的最小生成树算法有Kruskal算法和Prim算法。
1. Kruskal算法:按照边的权重从小到大排序,依次将边加入生成树,如果加入的边与已选择的边不构成环,则加入,否则不加入。
2. Prim算法:首先选择权重最小的边加入生成树,然后从剩余的边中选择一条与已选择的边相连且权重最小的边加入生成树,直到所有边都加入生成树。
例题2:一个通信网络由若干个节点和边组成,节点代表城市,边代表通信线路。
已知各个城市之间的距离和通信需求,请使用最小生成树算法为该通信网络设计一个最优的通信线路网。
参考答案:最小生成树算法可以用于解决此类问题。
通过最小生成树算法,我们可以找到一个包含所有节点且边的总权重最小的树形结构,以满足各个城市之间的通信需求。
常用的最小生成树算法有Kruskal算法和Prim算法。
1. Kruskal算法:按照边的权重从小到大排序,依次将边加入生成树,如果加入的边与已选择的边不构成环,则加入,否则不加入。
2. Prim算法:首先选择权重最小的边加入生成树,然后从剩余的边中选择一条与已选择的边相连且权重最小的边加入生成树,直到所有边都加入生成树。
例题3:一个城市的电力网由多个节点和边组成,节点代表发电厂或变电站,边代表输电线路。
已知各个节点之间的电抗和传输功率,请使用最小生成树算法为该城市电力网设计一个最优的输电线路。
参考答案:最小生成树算法可以用于解决此类问题。
通过最小生成树算法,我们可以找到一个包含所有节点且边的总电抗最小的树形结构,以满足各个节点之间的电力传输需求。
常用的最小生成树算法有Kruskal算法和Prim算法。
1. Kruskal算法:按照边的电抗从小到大排序,依次将边加入生成树,如果加入的边与已选择的边不构成环,则加入,否则不加入。
数据结构与算法——克鲁斯卡尔(Kruskal)算法
数据结构与算法——克鲁斯卡尔(Kruskal)算法⽬录应⽤场景-公交站问题某城市新增 7 个站点(A, B, C, D, E, F, G) ,现在需要修路把 7 个站点连通,各个站点的距离⽤边线表⽰(权) ,⽐如 A – B 距离 12公⾥问:如何修路保证各个站点都能连通,并且总的修建公路总⾥程最短?如上图所⽰:要求和前⾯的普利姆算法中的修路问题是⼀样的要求,只是换了⼀个背景。
克鲁斯卡尔算法介绍克鲁斯卡尔(Kruskal)算法,是⽤来求加权连通图的最⼩⽣成树的算法。
基本思想:按照权值从⼩到⼤的顺序选择n-1条边,并保证这n-1条边不构成回路具体做法:⾸先构造⼀个只含 n 个顶点的森林然后依权值从⼩到⼤从连通⽹中选择边加⼊到森林中,并使森林中不产⽣回路,直⾄森林变成⼀棵树为⽌克鲁斯卡尔算法图解在含有 n 个顶点的连通图中选择 n-1 条边,构成⼀棵极⼩连通⼦图,并使该连通⼦图中 n-1 条边上权值之和达到最⼩,则称其为连通⽹的最⼩⽣成树。
例如,对于如上图 G4 所⽰的连通⽹可以有多棵权值总和不相同的⽣成树。
有多种不同的连通⽅式,但是哪⼀种权值才是最优的呢?下⾯是克鲁斯卡尔算法的图解步骤:以上图 G4 为例,来对克鲁斯卡尔进⾏演⽰(假设,⽤数组 R 保存最⼩⽣成树结果)。
第 1 步:将边E,F [2]加⼊ R 中。
边E,F的权值最⼩,因此将它加⼊到最⼩⽣成树结果 R 中。
第 2 步:将边C,D [3]加⼊ R 中。
上⼀步操作之后,边C,D的权值最⼩,因此将它加⼊到最⼩⽣成树结果 R 中。
第 3 步:将边D,E [4]加⼊ R 中。
同理,权值最⼩第 4 步:将边B,F [7]加⼊ R 中。
上⼀步操作之后,边C,E [5]的权值最⼩,但C,E会和已有的边构成回路;因此,跳过边C,E。
同理,跳过边C,F [6]。
将边B,F加⼊到最⼩⽣成树结果R中。
第 5 步:将边E,G [8]加⼊ R 中。
同理第 6 步:将边A,B [12]加⼊ R 中。
Kruskal算法正确性证明
Kruskal算法正确性证明Kruskal算法: 步骤1,选择边e1,使得权值w(e1)尽可能⼩; 步骤2,若已选定边e1,e2,...,ei,则从E\{e1,e2,...,ei}选取e(i+1),使得 (1)G[{e1,e2,...,e(i+1)}]为⽆圈图 (2)权值w(e(i+1))是满⾜(1)的尽可能⼩的权; 步骤3,当步骤2不能继续执⾏时停⽌。
证明:由Kruskal算法构成的任何⽣成树T*=G[{e1,e2,...,e(n-1)}]都是最下⽣成树,这⾥n为赋权图G的顶点数。
(个⼈理解:因为局部最优,取尽可能⼩的权,不代表全局最⼩,因此正确性还是要证明的吧)使⽤反证法,1、有kruskal算法构成的⽣成树T*和异于T*的⽣成树T,这两种⽣成树。
2、定义函数f(T)表⽰不在T中的最⼩权值i的边ei。
假设T*不是最⼩树,T真正的最⼩树,显然T会使f(T)尽可能⼤的,即T本⾝权重则会尽可能⼩,。
3、设f(T)=k,表⽰存在⼀个不在T中的最⼩权值边ek=k,也就是说e1,e2,...e(k-1)同时在T和T*中,ek=k不在T中 4、T+ek包含唯⼀圈C。
设ek ' 是C的⼀条边,他在T中⽽不在T*中。
(想象圈C中⾄少有ek 和ek ' ,其中ek是⼜Kruskal算法得出的最⼩权边) 5、令T ' =W(T)+w(ei)-w(ei ' ),kruskal算法选出的是最⼩权边ek,(⽽ek'是T⾃⼰根据f(T)选出来的边)有w(ek ' )>=w(ek) 且W(T ' )=W(T*)(T ' 也是⼀个最⼩⽣成树) 6、但是f(T ' )>k= f(T),即T没有做到使得f(T)尽可能⼤,不再是真正的最⼩树,所以T=T*,从⽽T*确实是⼀棵最⼩数。
prim算法和kruskal算法例题
一、概述在图论中,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)
数据结构(三⼗三)最⼩⽣成树(Prim、Kruskal) ⼀、最⼩⽣成树的定义 ⼀个连通图的⽣成树是⼀个极⼩的连通⼦图,它含有图中全部的顶点,但只有⾜以构成⼀棵树的n-1条边。
在⼀个⽹的所有⽣成树中,权值总和最⼩的⽣成树称为最⼩代价⽣成树(Minimum Cost Spanning Tree),简称为最⼩⽣成树。
构造最⼩⽣成树的准则有以下3条:只能使⽤该图中的边构造最⼩⽣成树当且仅当使⽤n-1条边来连接图中的n个顶点不能使⽤产⽣回路的边 对⽐两个算法,Kruskal算法主要是针对边来展开,边数少时效率会⾮常⾼,所以对于稀疏图有很⼤的优势;⽽Prim算法对于稠密图,即边数⾮常多的情况会更好⼀些。
⼆、普⾥姆(Prim)算法 1.Prim算法描述 假设N={V,{E}}是连通⽹,TE是N上最⼩⽣成树中边的集合。
算法从U={u0,u0属于V},TE={}开始。
重复执⾏下⾯的操作:在所有u属于U,v 属于V-U的边(u,v)中找⼀条代价最⼩的边(u0,v0)并加⼊集合TE,同时v0加⼊U,直到U=V为⽌。
此时TE中必有n-1条边,则T=(V,{TE})为N的最⼩⽣成树。
2.Prim算法的C语⾔代码实现/* Prim算法⽣成最⼩⽣成树 */void MiniSpanTree_Prim(MGraph G){int min, i, j, k;int adjvex[MAXVEX]; /* 保存相关顶点下标 */int lowcost[MAXVEX]; /* 保存相关顶点间边的权值 */lowcost[0] = 0;/* 初始化第⼀个权值为0,即v0加⼊⽣成树 *//* lowcost的值为0,在这⾥就是此下标的顶点已经加⼊⽣成树 */adjvex[0] = 0; /* 初始化第⼀个顶点下标为0 */for(i = 1; i < G.numVertexes; i++) /* 循环除下标为0外的全部顶点 */{lowcost[i] = G.arc[0][i]; /* 将v0顶点与之有边的权值存⼊数组 */adjvex[i] = 0; /* 初始化都为v0的下标 */}for(i = 1; i < G.numVertexes; i++){min = INFINITY; /* 初始化最⼩权值为∞, *//* 通常设置为不可能的⼤数字如32767、65535等 */j = 1;k = 0;while(j < G.numVertexes) /* 循环全部顶点 */{if(lowcost[j]!=0 && lowcost[j] < min)/* 如果权值不为0且权值⼩于min */{min = lowcost[j]; /* 则让当前权值成为最⼩值 */k = j; /* 将当前最⼩值的下标存⼊k */}j++;}printf("(%d, %d)\n", adjvex[k], k);/* 打印当前顶点边中权值最⼩的边 */lowcost[k] = 0;/* 将当前顶点的权值设置为0,表⽰此顶点已经完成任务 */for(j = 1; j < G.numVertexes; j++) /* 循环所有顶点 */{if(lowcost[j]!=0 && G.arc[k][j] < lowcost[j]){/* 如果下标为k顶点各边权值⼩于此前这些顶点未被加⼊⽣成树权值 */lowcost[j] = G.arc[k][j];/* 将较⼩的权值存⼊lowcost相应位置 */adjvex[j] = k; /* 将下标为k的顶点存⼊adjvex */}}}}Prim算法 3.Prim算法的Java语⾔代码实现package bigjun.iplab.adjacencyMatrix;/*** 最⼩⽣成树之Prim算法*/public class MiniSpanTree_Prim {int lowCost; // 顶点对应的权值public CloseEdge(Object adjVex, int lowCost) {this.adjVex = adjVex;this.lowCost = lowCost;}}private static int getMinMum(CloseEdge[] closeEdges) {int min = Integer.MAX_VALUE; // 初始化最⼩权值为正⽆穷int v = -1; // 顶点数组下标for (int i = 0; i < closeEdges.length; i++) { // 遍历权值数组,找到最⼩的权值以及对应的顶点数组的下标if (closeEdges[i].lowCost != 0 && closeEdges[i].lowCost < min) {min = closeEdges[i].lowCost;v = i;}}return v;}// Prim算法构造图G的以u为起始点的最⼩⽣成树public static void Prim(AdjacencyMatrixGraphINF G, Object u) throws Exception{// 初始化⼀个⼆维最⼩⽣成树数组minSpanTree,由于最⼩⽣成树的边是n-1,所以数组第⼀个参数是G.getVexNum() - 1,第⼆个参数表⽰边的起点和终点符号,所以是2 Object[][] minSpanTree = new Object[G.getVexNum() - 1][2];int count = 0; // 最⼩⽣成树得到的边的序号// 初始化保存相关顶点和相关顶点间边的权值的数组对象CloseEdge[] closeEdges = new CloseEdge[G.getVexNum()];int k = G.locateVex(u);for (int j = 0; j < G.getVexNum(); j++) {if (j!=k) {closeEdges[j] = new CloseEdge(u, G.getArcs()[k][j]);// 将顶点u到其他各个顶点权值写⼊数组中}}closeEdges[k] = new CloseEdge(u, 0); // 加⼊u到⾃⾝的权值0for (int i = 1; i < G.getVexNum(); i++) { // 注意,这⾥从1开始,k = getMinMum(closeEdges); // 获取u到数组下标为k的顶点的权值最短minSpanTree[count][0] = closeEdges[k].adjVex; // 最⼩⽣成树第⼀个值为uminSpanTree[count][1] = G.getVexs()[k]; // 最⼩⽣成树第⼆个值为k对应的顶点count++;closeEdges[k].lowCost = 0; // 下标为k的顶点不参与最⼩权值的查找了for (int j = 0; j < G.getVexNum(); j++) {if (G.getArcs()[k][j] < closeEdges[j].lowCost) {closeEdges[j] = new CloseEdge(G.getVex(k), G.getArcs()[k][j]);}}}System.out.print("通过Prim算法得到的最⼩⽣成树序列为: {");for (Object[] Tree : minSpanTree) {System.out.print("(" + Tree[0].toString() + "-" + Tree[1].toString() + ")");}System.out.println("}");}} 4.举例说明Prim算法实现过程 以下图为例: 测试类:// ⼿动创建⼀个⽤于测试最⼩⽣成树算法的⽆向⽹public static AdjacencyMatrixGraphINF createUDNByYourHand_ForMiniSpanTree() {Object vexs_UDN[] = {"V0", "V1", "V2", "V3", "V4", "V5", "V6", "V7", "V8"};int arcsNum_UDN = 15;int[][] arcs_UDN = new int[vexs_UDN.length][vexs_UDN.length];for (int i = 0; i < vexs_UDN.length; i++) // 构造⽆向图邻接矩阵for (int j = 0; j < vexs_UDN.length; j++)if (i==j) {arcs_UDN[i][j]=0;} else {arcs_UDN[i][j] = arcs_UDN[i][j] = INFINITY;}arcs_UDN[0][5] = 11;arcs_UDN[1][2] = 18;arcs_UDN[1][6] = 16;arcs_UDN[1][8] = 12;arcs_UDN[2][3] = 22;arcs_UDN[2][8] = 8;arcs_UDN[3][4] = 20;arcs_UDN[3][6] = 24;arcs_UDN[3][7] = 16;arcs_UDN[3][8] = 21;arcs_UDN[4][5] = 26;arcs_UDN[4][7] = 7;arcs_UDN[5][6] = 17;arcs_UDN[6][7] = 19;for (int i = 0; i < vexs_UDN.length; i++) // 构造⽆向图邻接矩阵for (int j = i; j < vexs_UDN.length; j++)arcs_UDN[j][i] = arcs_UDN[i][j];return new AdjMatGraph(GraphKind.UDN, vexs_UDN.length, arcsNum_UDN, vexs_UDN, arcs_UDN);}public static void main(String[] args) throws Exception {AdjMatGraph UDN_Graph = (AdjMatGraph) createUDNByYourHand_ForMiniSpanTree();MiniSpanTree_Prim.Prim(UDN_Graph, "V0");} 输出为:通过Prim算法得到的最⼩⽣成树序列为: {(V0-V1)(V0-V5)(V1-V8)(V8-V2)(V1-V6)(V6-V7)(V7-V4)(V7-V3)} 分析算法执⾏过程:从V0开始:-count为0,k为0,closeEdges数组的-lowCost为{0 10 INF INF INF 11 INF INF INF},adjVex数组为{V0,V0,V0,V0,V0,V0,V0,V0,V0}-⽐较lowCost,于是k为1,adjVex[1]为V0,minSpanTree[0]为(V0,V1),lowCost为{0 0 INF INF INF 11 INF INF INF}-k为1,与V1的权值⾏⽐较,得到新的-lowCost为:{0 0 18 INF INF 11 16 INF 12},adjVex数组为{V0,V0,V1,V0,V0,V0,V1,V0,V1}-⽐较lowCost,于是k为5,adjVex[5]为V0,minSpanTree[1]为(V0,V5),lowCost为{0 0 18 INF INF 0 16 INF 12}-k为5,与V5的权值⾏⽐较,得到新的-lowCost为{0 0 18 INF 26 0 16 INF 12},adjVex数组为{V0,V0,V1,V0,V5,V0,V1,V0,V1}-⽐较lowCost,于是k为8,adjVex[8]为V1,minSpanTree[2]为(V1,V8),lowCost为{0 0 18 INF INF 0 16 INF 0}... 三、克鲁斯卡尔(Kruskal)算法 1.Kruskal算法描述 Kruskal算法是根据边的权值递增的⽅式,依次找出权值最⼩的边建⽴的最⼩⽣成树,并且规定每次新增的边,不能造成⽣成树有回路,直到找到n-1条边为⽌。
kruskal算法
概述
• 求加权连通图的最小生成树的算法。kruskal算法 总共选择n- 1条边,所使用的贪婪准则是:从剩 下的边中选择一条不会产生环路的具有最小耗费 的边加入已选择的边的集合中。注意到所选取的 边若产生环路则不可能形成一棵生成树。kruskal 算法分e 步,其中e 是网络中边的数目。按耗费递 增的顺序来考虑这e 条边,每次考虑一条边。当 考虑某条边时,若将其加入到已选边的集合中会 出现环路,则将其抛弃,否则,将它选入。
procedure quicksort(l,r:integer); //快速排序 var x,i,j :integer; begin x:=edges[random(r-l+1)+l].len; i:=l;j:=r; repeat while edges[i].len<x do inc(i); while edges[j].len>x do dec(j); if i<=j then begin swap(i,j); inc(i); dec(j); end until i>j; if l<j then quicksort(l,j); if i<r then quicksort(i,r); end;
第一步我们要做的事情就是将所有的边的长度排序,用排序的结果作为我们选择 边的依据。这里再次体现了贪心算法的思想。资源排序,对局部最优的资源进行 选择。排序完成后,我们率先选择了边AD。这样我们的图就变成了
第二步,在,7,7。完成之后,图变成了这个样子。
下一步就是关键了。下面选择那条边呢? BC或者EF吗?都不是,尽管现在长度为8的边 是最小的未选择的边。但是他们已经连通了(对于BC可以通过CE,EB来连接,类似的EF 可以通过EB,BA,AD,DF来接连)。所以我们不需要选择他们。类似的BD也已经连通了( 这里上图的连通线用红色表示了)。最后就剩下EG和FG了。当然我们选择了EG。最后 成功的图就是下图:
Prim算法和Kruskal算法介绍
Prim算法和Kruskal算法介绍⼀、Prim算法普利姆(Prim)算法适⽤于求解⽆向图中的(Minimum Cost Spanning Tree)。
下⾯是Prim算法构造最⼩⽣成树的过程图解。
选择⼀个节点开始,⽐如V1进⼊集合U,剩下的集合的V-U包括剩下的节点,然后寻找从集合U到集合V-U最近的路径。
这⾥有三条路径分别是权重为6到V2,权重为5到V4以及权重为1到V3,显然到通过V3连接⽽集合U和集合V-U是最近的,选择V3进⼊集合U。
同样继续选择到V-U的路径,此时有6条可选路径,分别是权为6到V2【从V1】,权为5到V4【从V1】,权为5到V2【从V3】,权为5到V4【从V3】,权为6到V5【从V3】,权为4到V6【从V3】。
选择出从V3到V6的路径并将V6添加⾄集合U中。
按照这种⽅法依次将V4,V2和V5添加到集合U直到U和全体节点结合V相等,或者说V-U集合为空时结束,这时选出的n-1条边即为最⼩⽣成树。
⼆、Kruskal算法克鲁斯卡尔(Kruskal)算法是另⼀种求解最⼩⽣成树的算法。
下⾯是Kruskal算法构造最⼩⽣成树的过程图解。
Kruskal则是采取另⼀种思路,即从边⼊⼿。
⾸先n个顶点分别视为n个连通分量,然后选择⼀条权重最⼩的边,如果边的两端分属于两个连通分量,就把这个边加⼊集合E,否则舍去这条边⽽选择下⼀条代价最⼩的边,依次类推,直到所有节点都在同⼀个连通分量上。
三、对⽐假设⽹中有n个节点和e条边,普利姆算法的时间复杂度是O(n^2),克鲁斯卡尔算法的时间复杂度是O(eloge),可以看出前者与⽹中的边数⽆关,⽽后者相反。
因此,普利姆算法适⽤于边稠密的⽹络⽽克鲁斯卡尔算法适⽤于求解边稀疏的⽹。
最小支撑树算法
最小支撑树算法最小支撑树(Minimum Spanning Tree,简称MST)是一种图论算法,用于寻找一个无向图中的一棵包含所有顶点的树,并且该树的所有边的权值之和最小。
MST算法在解决网络设计、电力传输、通信网络、城市规划等问题中具有重要的应用价值。
本文将介绍Prim算法和Kruskal算法,它们是最常用的解决MST问题的算法。
首先,我们来介绍Prim算法。
Prim算法从一个起始点开始,逐步选取与已生成树相邻且权值最小的边,将其加入MST中。
具体步骤如下:(1)初始化一个空的树,将起始点加入树中;(2)从剩余顶点中找出与树相邻且权值最小的边,将其加入树中;(3)重复步骤(2),直到所有顶点都加入树中。
这样,我们就能得到最小权值的支撑树。
其次,我们来介绍Kruskal算法。
Kruskal算法首先将所有边按权值从小到大排序,然后逐个加入树中,但要确保加入的边不会形成环路。
具体步骤如下:(1)初始化一个空的树,将所有顶点看作独立的树;(2)从权值最小的边开始,如果该边的两个顶点不在同一棵树中,则将其加入树中;(3)重复步骤(2),直到所有顶点都加入树中。
这样,我们也能得到最小权值的支撑树。
要注意的是,Prim算法适用于稠密图,由于其每次都要找到与已生成树相邻且权值最小的边,因此其时间复杂度为O(V^2),其中V是顶点数。
而Kruskal算法适用于稀疏图,由于其每次只需要找到当前权值最小的边,因此其时间复杂度为O(ElogE),其中E是边数。
最后,我们来总结一下最小支撑树算法的应用意义。
MST算法在实际应用中有着广泛的应用,它不仅可以用于网络设计、电力传输、通信网络和城市规划等领域,还可以应用于数据聚类、图像分割、机器学习等问题。
通过构建最小支撑树,我们可以在保证连接所有顶点的前提下,尽量减少总权值,从而达到经济、高效的目的。
综上所述,最小支撑树算法是一种重要的图论算法,通过Prim算法和Kruskal算法可以寻找到包含所有顶点的树,并且该树的所有边的权值之和最小。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Kruskal算法假设给定一个加权连通图G,G的边集合为E,顶点个数为n,要求其一棵最小生成树T。
Kruskal算法的粗略描述:假设T中的边和顶点均涂成红色,其余边为白色。
开始时G中的边均为白色。
1)将所有顶点涂成红色;2)在白色边中,挑选一条权最小的边,使其与红色边不形成圈,将该白色边涂红;3)重复2)直到有n-1条红色边,这n-1条红色边便构成最小生成树T的边集合。
注意到在算法执行过程中,红色顶点和红色边会形成一个或多个连通分支,它们都是G的子树。
一条边与红色边形成圈当且仅当这条边的两个端点属于同一个子树。
因此判定一条边是否与红色边形成圈,只需判断这条边的两端点是否属于同一个子树。
上述判断可以如此实现:给每个子树一个不同的编号,对每一个顶点引入一个标记t,表示这个顶点所在的子树编号。
当加入一条红色边,就会使该边两端点所在的两个子树连接起来,成为一个子树,从而两个子树中的顶点标记要改变成一样。
综上,可将Kruskal算法细化使其更容易计算机实现。
C代码/*Kruskal.cCopyright(c)2002,2006by ctu_85All Rights Reserved.Highlight by yzfy雨中飞燕*//*I am sorry to say that the situation of unconnected graph is not concerned*/#include"stdio.h"#define maxver10#define maxright100int G[maxver][maxver],record=0,touched[maxver][maxver];int circle=0;int FindCircle(int,int,int,int);int main(){int path[maxver][2],used[maxver][maxver];int i=0,j=0,k=0,t,min=maxright,exsit=0;int v1,v2,num,temp,status=0;restart:printf("Please enter the number of vertex(s)in the graph:\n");scanf("%d",&num);if(num>maxver||num<0){printf("Error!Please reinput!\n");goto restart;}for(j=0;j<num;j++)for(k=0;k<num;k++)if(j==k){G[j][k]=maxright;used[j][k]=1;touched[j][k]=0;}elseif(j<k){re:printf("Please input the right between vertex%d and vertex%d,if no edge exists please input-1:\n",j+1,k+1);scanf("%d",&temp);if(temp>=maxright||temp<-1){printf("Invalid input!\n");goto re;}if(temp==-1)temp=maxright;G[j][k]=G[k][j]=temp;used[j][k]=used[k][j]=0;touched[j][k]=touched[k][j]=0;}}for(j=0;j<num;j++){path[j][0]=0;path[j][1]=0;}for(j=0;j<num;j++){status=0;for(k=0;k<num;k++)if(G[j][k]<maxright){status=1;break;}if(status==0)break;for(i=0;i<num-1&&status;i++){for(j=0;j<num;j++)for(k=0;k<num;k++)if(G[j][k]<min&&!used[j][k]){v1=j;v2=k;min=G[j][k];}if(!used[v1][v2]){used[v1][v2]=1;used[v2][v1]=1;touched[v1][v2]=1;touched[v2][v1]=1;path[0]=v1;path[1]=v2;for(t=0;t<record;t++)FindCircle(path[t][0],path[t][0],num,path[t][0]);if(circle){/*if a circle exsits,roll back*/circle=0;i--;exsit=0;touched[v1][v2]=0;touched[v2][v1]=0;min=maxright;}else{record++;min=maxright;}}}if(!status)printf("We cannot deal with it because the graph is not connected!\n"); else{for(i=0;i<num-1;i++)printf("Path%d:vertex%d to vertex%d\n",i+1,path[0]+1,path[1]+1);}return1;}int FindCircle(int start,int begin,int times,int pre){/*to judge whether a circle is produced*/int i;for(i=0;i<times;i++)if(touched[begin]==1){if(i==start&&pre!=start){circle=1;return1;break;}elseif(pre!=i)FindCircle(start,i,times,begin);elsecontinue;}return1;}[编辑本段]pascal例程USER:BO LIU[raulliu2]TASK:agrinetLANG:PASCALCompiling...Compile:OKExecuting...Test1:TEST OK[0secs]Test2:TEST OK[0secs]Test3:TEST OK[0secs]Test4:TEST OK[0.004secs]Test5:TEST OK[0.004secs]Test6:TEST OK[0secs]Test7:TEST OK[0.004secs]Test8:TEST OK[0.004secs]Test9:TEST OK[0.004secs]Test10:TEST OK[0.012secs]All tests OK.Your program('agrinet')produced all correct answers!This is yoursubmission#4for this problem.Congratulations!标准MST,啥优化都不要。
刚开始用kruskal做的。
不知道怎么回事老是WA on test5....很郁闷的说。
只得改用prim。
我可不喜欢prim啊。
my ugly code:PRIM:{PROG:agrinetID:parachutesLANG:PASCAL}varmap:array[1..100,1..100]of longint;d:array[1..100]of longint;mark:array[1..100]of boolean;n,i,ans,j:longint;procedure prim;vari,j,min,minj:longint;beginfillchar(mark,sizeof(mark),0);mark[1]:=true;for i:=1to n do beginif map[1,i]<>0then d:=map[1,i]else d:=maxlongint;end;ans:=0;for i:=2to n do beginmin:=maxlongint;for j:=1to n do beginif(not mark[j])and(d[j]<min)then beginmin:=d[j];minj:=j;end;end;mark[minj]:=true;inc(ans,d[minj]);for j:=1to n doif(d[j]>map[minj,j])and(map[minj,j]<>0)thend[j]:=map[minj,j];end;end;beginassign(input,'agrinet.in');reset(input);assign(output,'agrinet.out');rewrite(output);readln(n);for i:=1to n do beginfor j:=1to n doread(map[i,j]);readln;end;prim;writeln(ans);close(input);close(output);end.===================================强大的分割线===================================={PROG:agrinetID:parachutesLANG:PASCAL}varx,j,tot,i,n:longint;l,a,b:array[1..10000]of longint;f:array[1..100]of longint;v:array[1..100,1..100]of boolean;procedure qsort(ll,rr:longint);vari,j,mid,temp:longint;begini:=ll;j:=rr;mid:=l[(i+j)shr1];repeatwhile l<mid do inc(i);while l[j]>mid do dec(j);if i<=j then begintemp:=l;l:=l[j];l[j]:=temp;temp:=a;a:=a[j];a[j]:=temp;temp:=b;b:=b[j];b[j]:=temp;inc(i);dec(j);end;until i>j;if i<rr then qsort(i,rr);if ll<j then qsort(ll,j);end;function find(x:longint):longint;vartmp:longint;beginif f[x]=x then exit(x)else exit(find(f[x]));end;procedure union(u,v:longint);varfu,fv:longint;beginfu:=find(u);fv:=find(v);f[fv]:=fu;end;procedure kruskal;varcnt,i,ans:longint;beginfor i:=1to n do f:=i;cnt:=0;ans:=0;for i:=1to tot doif(find(a)<>find(b))then beginunion(a,b);ans:=ans+l;inc(cnt);if cnt=n-1then break;end;writeln(ans);end;beginassign(input,'agrinet.in');reset(input); assign(output,'agrinet.out');rewrite(output); fillchar(v,sizeof(v),0);tot:=0;readln(n);for i:=1to n do beginfor j:=1to n do beginread(x);if(not v[i,j])and(i<>j)then begininc(tot);a[tot]:=i;b[tot]:=j;v[i,j]:=true;v[j,i]:=true;l[tot]:=x;end;end;readln;end;qsort(1,tot);kruskal;close(input);close(output);end.KRUSKAL要加入并查集,判断点是否在同一个集合内,如果在则不能取该边!只适用与稀疏图。