Kruskal算法
- 格式:ppt
- 大小:423.50 KB
- 文档页数:12
Kruskal算法和Prim算法本节所阐述的两种最小生成树算法是上节所介绍的一般算法的细化。
每个算法均采用一特定规则来确定GENERIC-MST算法第3行所描述的安全边;在Kruskal算法中,集合A是一森林,加大集合A中的安全边总是图中连结两不同连通支的最小权边。
在Prim算法中,集合A仅形成单棵树。
添加入集合A的安全边总是连结树与一非树结点的最小权边。
Kruskal算法Kruskal算法是直接基于上一节中给出的一般最小生成树算法的基础之上的。
该算法找出森林中连结任意两棵树的所有边中具有最小权值的边(u,v)作为安全边,并把它添加到正在生长的森林中。
设C1和C2表示边(u,v)连结的两棵树。
因为(u,v)必是连C1和其他某棵树的一条轻边,所以由推论2可知(u,v)对C1是安全边。
Kruskal算法同时也是一种贪心算法,因为算法每一步添加到森林中的边的权值都尽可能小。
Kruskal算法的实现类似于计算连通支的算法。
它使用了分离集合数据结构以保持数个互相分离的元素集合。
每一集合包含当前森林中某个树的结点,操作FIND-SET(u)返回包含u的集合的一个代表元素,因此我们可以通过FIND-SET(v)来确定两结点u和v是否属于同一棵树,通过操作UNION来完成树与树的联结。
MST-KRUSKAL(G,w)1. A←∅2. for 每个结点v V[G]3. do MAKE-SET(v)4. 根据权w的非递减顺序对E的边进行排序5. for 每条边(u,v)∈E,按权的非递减次序6. do if FIND-SET(u) ≠ FIND-SET(v)7. then A←A∪{(u,v)}8. UNION(u,v)9 return AKruskal算法的工作流程如图4所示。
阴影覆盖的边属于正在生成的森林A。
算法按权的大小顺序考察各边。
箭头指向算法每一步所考察到的边。
第1-3行初始化集合A为空集并建立|V|棵树,每裸树包含图的一个结点。
贪心策略是指从问题的初始状态出发,通过若干次的贪心选择而得出最优值(或较优解)的一种解题方法。
Ⅰ、库鲁斯卡尔(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算法百科名片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),由于会产生环路,将其丢弃。
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 算法构造最小生成树构造赋权图(,,)G V E W =,其中12{,,,}n V v v v = 为顶点集合,其中i v 表示第i 个顶点,12{,,,,}i E e e e = 为边的集合,()ij n n W w ⨯=为邻接矩阵,其中i j i j ij i j v v v v w v v ∈⎧⎪=⎨∞⎪⎩顶点与之间的距离,当(,)E ,与之间没有边时科茹斯克尔(Kruskal )算法如下: (1)选1e E ∈(E 为边的集合),使得1e 是权重最小的边。
(2)若12,,e i e e …,已选好,则从12{,,e }i E e e -…,中选取1i e +,使得 i )121{,,e }i e e +…,中无圈,且 ii )是1i e +是12{,,e }i E e e -…,中权重最小的边。
(3)直到选得1V e - 为止。
(V 是集合V 中元素的个数)例:已知9个村庄之间的两两距离见表5,要架设通讯线路把9个村庄连接起来,求所需要通讯线的最小长度。
表5 10个村庄的两两距离数据(单位:km )(,,)G V E W =,其中129{,,,}V v v v = 为顶点集合,其中i v 表示第i 个村庄,12{,,,,}i E e e e = 为边的集合,99()ij W w ⨯=为邻接矩阵,其中i j i j ij i j v v v v w v v ∈⎧⎪=⎨∞⎪⎩村庄与之间的距离,当(,)E ,与之间没有通讯路线时科茹斯克尔(Kruskal )算法如下: (1)选1e E ∈(E 为边的集合),使得1e 是距离最小的边。
(2)若12,,e i e e …,已选好,则从12{,,e }i E e e -…,中选取1i e +,使得 i )121{,,e }i e e +…,中无圈,且 ii )是1i e +是12{,,e }i E e e -…,中距离最小的边。
(3)直到选得8e 为止。
贪心策略是指从问题的初始状态出发,通过若干次的贪心选择而得出最优值(或较优解)的一种解题方法。
Ⅰ、库鲁斯卡尔(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算法回环判断方法(原创实用版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算法求解最小生成树,也以此检验自己一学期所学成果,加深对算法设计与分析这门课程的理解,由于所学知识有限,难免有些繁琐和不完善之处,下面向大家介绍此程序的设计原理,方法,内容及设计的结果。
本程序是在windows 环境下利用Microsoft Visual C++ 6.0所编写的,主要利用贪心算法的思想,以及数组,for语句的循环,if语句的嵌套,运用以上所学知识编写出的prim和kruskal算法求解最小生成树,在输入其边的起始位置,种植位置以及权值后,便可分别输出此网的prim和kruskal算法最小生成树的边的起始位置,终止位置以及权值。
正文2.1 设计方法和内容一.软件环境:Microsoft Visual C++ 6.0二.详细设计思想:所谓贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择。
也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。
贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。
贪心算法的基本思路如下:1.建立数学模型来描述问题。
2.把求解的问题分成若干个子问题。
3.对每一子问题求解,得到子问题的局部最优解。
4.把子问题的解局部最优解合成原来解问题的一个解。
1.Prim(普里姆)算法思想无向网的最小生成树问题此算法的思想是基于点的贪心,力求从源点到下一个点的距离最短,以此来构建临接矩阵,所以此算法的核心思想是求得源点到下一个点的最短距离,下面具体解释如何求此最短距离:在图中任取一个顶点k作为开始点,令集合U={k},集合w=V-U,其中v为图中所有顶点的集合,然后找出:一个顶点在集合U中,另一个顶点在集合W中的所有边中,权值最短的一条边,,并将该边顶点全部加入集合U中,并从W中删去这些顶点,然后重新调整U中顶点到W中顶点的距离,使之保持最小,在重复此过程,直到W为空集为止,求解过程如下:由图可知最小生成树的步骤,假设开始顶点就选为1,故首先有u={1},w={2,3,4,5}。
最小生成树算法比较Prim和Kruskal算法的优劣在图论中,最小生成树(Minimum Spanning Tree, MST)是指一个连通图的生成树,它的所有边的权值之和最小。
最小生成树算法是解决最小生成树问题的常用方法,而Prim算法和Kruskal算法是两种经典的最小生成树算法。
本文将比较Prim算法和Kruskal算法的优劣,为读者提供更全面的了解。
一、Prim算法Prim算法是一种贪心算法,通过逐步扩展生成树的方式来构建最小生成树。
Prim算法以一个初始节点开始,然后逐渐添加与当前生成树相连的最短边,直到生成树包含图中的所有节点为止。
以下是Prim算法的基本步骤:1. 选择任意一个节点作为初始节点,并将其加入生成树中。
2. 从生成树的节点中选择一个最短边,并将与该边相连的节点加入生成树。
3. 重复步骤2,直到生成树中包含所有节点。
相比于Kruskal算法,Prim算法在每一步只考虑一个节点,并且每次选择最短边,因此Prim算法的时间复杂度为O(V^2),其中V是图中的节点数。
二、Kruskal算法Kruskal算法也是一种贪心算法,通过按照边的权值递增的顺序来构建最小生成树。
Kruskal算法从图中所有边中选择最短边,并将其加入生成树中,同时保证生成树不形成环,直到生成树中包含所有节点为止。
以下是Kruskal算法的基本步骤:1. 对图中的所有边按照权值进行排序。
2. 依次遍历排序后的边,将权值最小的边加入生成树中,并检查是否形成环。
3. 重复步骤2,直到生成树中包含所有节点。
Kruskal算法中的关键步骤是判断是否形成环,可以使用并查集数据结构来实现。
Kruskal算法的时间复杂度为O(ElogE),其中E是图中的边数。
三、Prim算法与Kruskal算法的比较1. 时间复杂度:Prim算法的时间复杂度为O(V^2),而Kruskal算法的时间复杂度为O(ElogE)。
由于E通常小于V^2,所以Kruskal算法在大多数情况下更快。