Kruskal算法求最小生成树
- 格式:doc
- 大小:60.50 KB
- 文档页数:8
Kruskal克鲁斯卡尔最⼩⽣成树算法(贪⼼算法)1. /*2. * Introduction to Algorithms3. * Chapter 23 --- MST.Kruskal4. * Tanky Woo @ 5. * 2012.1.76. */7.8. #include <</span>iostream>9. #include <</span>algorithm>10. using namespace std;11.12. const int maxint = 999999;13.14. typedef struct Road{15. int c1, c2;// a到b16. int value;//权值17. }Road;18.19. int no;20. int line;//记录实际关系数21. Road road[100];//设⼀个⽐较⼤的值,实际看输⼊最⼩⽣成树中:边数e=n-122. int node[101];//最⼩⽣成树:n顶点数23.24. bool myCmp(const Road &a,const Road &b)25. {26. if(a.value <</span> b.value)27. return 1;28. return 0;29. }30.31. //node[2]=1 node[8]=1 ,node[3]=1,共同的祖先,如果(3,8)加进去,则构成回路,不要32. //有点像并查集33. int Find_Set(int n)34. {35. if(node[n]==-1)36. return n;37. return node[n]= Find_Set(node[n]);38. }39.40. bool Merge(int s1,int s2)41. {42. int r1 = Find_Set(s1);43. int r2 = Find_Set(s2);44. if(r1 == r2)//如果相等证明构成回路,则直接返回⼀个0,不要把顶点加进来(下⼀步是加进去的)45. return 0;46. if(r1 <</span> r2)47. node[r2]= r1;48. else49. node[r1]= r2;50. return 1;51. }52.53. int main()54. {55. freopen("input.txt","r", stdin);56. //初始化全为-157. memset(node,-1, sizeof(node));58. scanf("%d",&no);59. scanf("%d",&line);60. int i;61. for(i=0; i<</span>line; ++i)62. {63. cin >> road[i].c1 >> road[i].c2 >> road[i].value;64. }65. sort(road, road+line, myCmp);66. int sum = 0, count = 0;// sum是MST的值,count是记录已使⽤的点数67. for(i=0; i<</span>line; ++i)68. {69. if(Merge(road[i].c1, road[i].c2))//如果返回的为0,则证明构成回路,不要加进70. {71. count ++;72. sum += road[i].value;73. }74. if(count == no-1)//e=n-1已经连通,可以退出75. break;76. }77. cout <</span><</span> sum <</span><</span> endl;78. return 0;79. }80.81.82. /*83. input.txt:84. 985. 1486. 1 2 487. 1 8 888. 2 3 889. 2 8 1190. 3 4 791. 3 6 492. 3 9 293. 4 5 994. 4 6 1495. 5 6 1096. 6 7 297. 7 8 198. 7 9 699. 8 9 7100. */。
如何使用Kruskal算法求解最小生成树Kruskal算法是一种求解图的最小生成树的算法,算法简单易懂,效率高,被广泛应用于计算机科学领域。
本文将从算法背景、算法原理、算法流程、时间复杂度等方面,详细介绍如何使用Kruskal算法求解最小生成树。
一、算法背景在图论中,一张图由节点和边组成。
最小生成树是指在一张连接所有节点的图中,选出n-1条边,使得边的权值之和最小。
最小生成树在实际问题中具有非常广泛的应用,如电力网络规划、通信网络规划等。
二、算法原理Kruskal算法是一种基于贪心算法的最小生成树算法。
其基本思想是,先将图中的所有边按照权值大小排序,然后依次选择权值最小的边,直到选出n-1条边为止。
同时,该算法还要保证所选的边不会与已选的边形成回路,以保证生成的树是无向树。
三、算法流程Kruskal算法的流程如下:1. 将图中所有边按照权值大小排序2. 从小到大选择边,并将所选的边加入到生成树中3. 如果加入这条边后,生成树中出现了回路,则不选择这条边4. 重复步骤2和步骤3,直到选出n-1条边为止四、实现细节1. 图的表示Kruskal算法需要使用图的邻接表或邻接矩阵来表示图结构。
在本文中,我们采用邻接表来表示图。
2. 边的排序将所有边按照权值大小从小到大排序,可以使用快速排序等算法进行排序。
3. 并查集并查集是实现Kruskal算法的关键。
在算法中需要对于每一条边判断其加入生成树后是否会形成回路。
实现简单的方法是使用并查集。
4. 判断连通性使用并查集不仅可以判断是否形成回路,还可以用来判断节点之间的连通性。
在算法中,当选取n-1条边后,若生成的树不连通,则原图一定不连通。
五、时间复杂度由于Kruskal算法使用了并查集,所以时间复杂度为O(m log n),其中,m为边数,n为节点数。
相比于其他最小生成树算法,Kruskal算法具有较高的效率和稳定性。
六、总结Kruskal算法是一种简单、高效的最小生成树算法。
Kruskal算法是一种用于求解最小生成树的算法,它的主要特点是按边的权值大小进行排序,并逐步添加边,直到构成一棵生成树为止。
在实际应用中,Kruskal算法通常被用来解决各种最小生成树的问题,比如公路修建、通信网络的建设、电路板的设计等等。
本文将着重介绍Kruskal算法的原理、步骤和具体应用,以期为读者提供全面的了解和指导。
一、Kruskal算法的原理Kruskal算法是一种基于贪心策略的算法,它通过不断地选择边来构建生成树,直到所有的顶点都被包含在生成树中。
其基本原理可以概括如下:1. 将图中的所有边按权值大小进行排序。
2. 依次从小到大选择边,并检查是否形成环路。
3. 如果没有形成环路,则将该边添加到生成树中。
Kruskal算法的核心思想是“先小后大”,即先选择权值较小的边,直到所有的顶点都被包含在生成树中。
由于Kruskal算法是基于边来构建生成树的,因此它适用于稀疏图和稠密图,而且在实际应用中通常具有较好的效率。
二、Kruskal算法的步骤Kruskal算法的具体步骤可以简单总结为以下几个步骤:1. 初始化:将图中的所有边按权值大小进行排序。
2. 创建一个空的数组T来存储最小生成树的边。
3. 依次从排序后的边集合中选择边e,并检查是否添加e会形成环路。
4. 如果不形成环路,则将边e添加到数组T中。
5. 直到T中包含了n-1条边为止,其中n为顶点的个数。
三、Kruskal算法的具体实现下面我们通过一个简单的示例来说明Kruskal算法的具体实现步骤。
示例:假设有如下的图G和边集合E:顶点集合V:{A, B, C, D, E, F, G, H}边集合E:{(A, B, 4), (A, H, 8), (B, C, 8), (B, H, 11), (C, D, 7), (C, F, 4), (C, I, 2), (D, E, 9), (D, F, 14), (E, F, 10), (F, G, 2), (G, H, 1), (G, I, 6), (H, I, 7)}我们可以按照如下的步骤来求解最小生成树:1. 首先对边集合E进行按权值的排序:(E, F, 2), (G, H, 1), (C, I, 2), (A, B, 4), (C, F, 4), (H, I, 7), (C, D, 7), (D, E, 9), (A, H, 8), (B, C, 8), (G, I, 6), (B, H, 11), (D, F, 14)2. 初始化一个空的数组T来存储最小生成树的边,初始化一个数组parent来记录每个顶点的父节点。
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算法。
该算法的主要步骤是:GENERNIC-MIT(G,W)1. A←2. while A没有形成一棵生成树3 do 找出A的一条安全边(u,v);4.A←A∪{(u,v)};5.return A算法设置了集合A,该集合一直是某最小生成树的子集。
在每步决定是否把边(u,v)添加到集合A中,其添加条件是A∪{(u,v)}仍然是最小生成树的子集。
我们称这样的边为A 的安全边,因为可以安全地把它添加到A中而不会破坏上述条件。
然后就是Dijkstra算法。
Dijkstra算法基本思路是:假设每个点都有一对标号 (d j, p j),其中d j是从起源点s到点j的最短路径的长度 (从顶点到其本身的最短路径是零路(没有弧的路),其长度等于零);p j则是从s到j的最短路径中j点的前一点。
求解从起源点s到点j的最短路径算法的基本过程如下:1) 初始化。
起源点设置为:① d s=0, p s为空;②所有其他点: d i=∞, p i=?;③标记起源点s,记k=s,其他所有点设为未标记的。
2) 检验从所有已标记的点k到其直接连接的未标记的点j的距离,并设置:d j=min[d j, d k+l kj]式中,l kj是从点k到j的直接连接距离。
3) 选取下一个点。
从所有未标记的结点中,选取d j中最小的一个i:d i=min[d j, 所有未标记的点j]点i就被选为最短路径中的一点,并设为已标记的。
4) 找到点i的前一点。
从已标记的点中找到直接连接到点i的点j*,作为前一点,设置:i=j*5) 标记点i。
c语言数据结构kruskal算法两种构造最小生成树算法1. 引言1.1 概述本篇文章将讨论C语言数据结构中的Kruskal算法以及它的两种构造最小生成树的方法。
数据结构是计算机科学中至关重要的概念,它用于组织和存储数据以便于使用和操作。
C语言是一种广泛应用于系统开发和嵌入式软件的编程语言,而Kruskal算法则是解决图论中最小生成树问题的一种有效方法。
1.2 文章结构本文共分为五个部分。
首先,我们将介绍C语言中的数据结构,包括其概念、在C语言中的实现以及在算法中应用的重要性。
接下来,我们将对Kruskal算法进行简要介绍,包括最小生成树的概念和应用场景、Kruskal算法原理及思路以及详细步骤解析。
然后,我们将探讨Kruskal算法的两种构造最小生成树的方法:按边权重顺序遍历并加入最小生成树中和使用并查集维护连通性信息并进行判断和合并操作。
最后,我们将总结评估这两种方法,并讨论适用情景与选择依据。
文章最后将给出结论与总结,并对该算法的效率、应用案例以及未来的可扩展和改进方向进行分析。
1.3 目的本文旨在深入介绍C语言数据结构中Kruskal算法的两种构造最小生成树的方法,并对它们进行比较与选择依据分析。
通过阅读本文,读者将能够理解数据结构在算法中的应用、掌握Kruskal算法的原理和步骤,并了解如何选择合适的构造最小生成树方法。
此外,本文还将讨论该算法的效率、实际应用案例以及未来可能的发展方向,以便读者更全面地了解该算法并从中受益。
2. C语言数据结构简介:2.1 数据结构概念数据结构是计算机存储、组织和管理数据的方式。
它涉及到如何将不同类型的数据元素组合在一起,以便有效地进行操作和处理。
常见的数据结构包括数组、链表、栈、队列、树等。
2.2 C语言中的数据结构C语言提供了基本的数据类型,如整型、浮点型和字符型等,同时还支持用户自定义的数据结构。
通过使用struct关键字可以定义自己的复合类型,其中可以包含多个不同类型的成员变量。
荆楚理工学院课程设计成果学院:_______计算机工程学院__________ 班级: 14计算机科学与技术一班学生姓名: 陈志杰学号: 2014407020137设计地点(单位)_____B5101_________ ____________设计题目:克鲁斯卡尔算法求最小生成树__________________________________完成日期:2015年1月6日指导教师评语: ______________ ____________________________________________________________________________________________________________ ___________________________________________________________________________________________ ___________________________ __________ _成绩(五级记分制):_____ _ __________教师签名:__________ _______________注:介于A和C之间为B级,低于C为D级和E级。
按各项指标打分后,总分在90~100为优,80~89为良,70~79为中,60~69为及格,60分以下为不及格。
目录1 需求分析 (1)1.1系统目标 (1)1.2主体功能 (1)1.3开发环境 (1)2 概要设计 (1)2.1功能模块划分 (1)2.2 系统流程图 (2)3 详细设计 (3)3.1 数据结构 (3)3.2 模块设计 (3)4测试 (3)4.1 测试数据 (3)4.2测试分析 (4)5总结与体会 (6)5.1总结: (6)5.2体会: (6)参考文献 (7)附录全部代码 (8)1 需求分析1.1系统目标Kruskal算法是一种按照网中边的权值递增的顺序构造最小生成树的方法。
用Kruskal算法求无向图的最小生成树该图用邻接矩阵表示,邻接表原理与之相同。
可以指出的是,对于有向图,算法可以做得更加简单,因为对无向图的“回边”情况的处理比有向图回边情况的处理要复杂一些。
图1:输入示例图二:输入时若两点之间没有公共边,则将权值设置为-1。
程序设置处理的最大点数为10。
图三:注意到Kruskal算法的解答结果有时候不是唯一的,这个结果和对图遍历时的顺序有关,但是必需注意的是所有的最小生成树其网络代价和是一样的。
下面是源代码:/* 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[i][0]=v1;path[i][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[i][0]+1,path[i][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][i]==1){if(i==start&&pre!=start){circle=1;return 1;break;}elseif(pre!=i)FindCircle(start,i,times,begin); elsecontinue;}return 1;}。
计算机科学与工程学院实验报告
实验题目:Kruskal算法求最小生成树
课程名称:离散数学
实验类型:设计性
专业:软件工程班级:学生姓名:学号:
实验日期:2011 年12 月8 日实验地点:
实验学时:实验成绩:
指导教师:***
[实验题目] Kruskal算法求最小生成树
[实验原理]
设所给定无向连通加权图具有n个结点,m条边,首先,将各条边的权按从小到大的顺序排序。
然后依次将这些边按所给图的结构放到生成树中去。
如果在放置某一条边时,使得生成树形成回路,则删除这条边。
这样,直至生成树具有n-1条边时,我们所得到的就是一棵最小生成树。
[实验步骤]
(1)边依小到大顺序得l
1,l
2
,…,l
m。
(2)置初值:⇒
∅S,0⇒i,1⇒j。
(3)若i=n-1,则转(6)。
(4)若生成树边集S并入一条新的边l
j
之后产生的回路,则j+1⇒j,并转(4)。
(5)否则,i+1⇒i;l j⇒S(i);j+1⇒j,转(3)。
(6)输出最小生成树S。
(7)结束。
[实验中遇到的问题及解决办法]
(1)出现回路:在前几次测试时,总会出现回路,经过动态跟踪检查出是在合
并两个连通分量时没有考虑该结点是否同时在两个连通分量中,导致合
并后形成回路。
解决办法是增加一个寻找顶点的双亲直到根结点,如果
根结点相同表明会形成回路不添加该边。
(2)生成的并不是最小生成树:原因在于开始手动排序权值后输入的边,
一旦不按权值由小到大输入边,就不会产生最小生成树。
解决办法,增
加一个以权值为关键值的排序函数,将边排序,然后再调用最小生成树
函数。
问题得以解决。
(3)出现多边的情况,即在此形成回路:动态跟踪得知,忽略了最小生成
树的一条重要特征:最后所剩边数应为顶点数-1增加一个if判断语
句结束循环后问题解决。
[程序清单及运行结果]
#include <iostream>
using namespace std;
const int MaxVertex = 20; //图中最大顶点数
const int MaxEdge = 100; //图中最大边数
int FindRoot(int parent[],int v);
struct EdgeType
{
int from; //边的起点
int to; //边的终点
int weight; //权值
};
struct EdgeGraph
{
char vertex[MaxVertex]; //顶点的数据类型为‘char’EdgeType edge[MaxEdge]; //存放边的数组
int vertexNum; //图的顶点数
int edgeNum; //图的边数
int parent[MaxVertex];
};
void Sort(EdgeType ed[],int E)//权值排序
{
int i,j;
EdgeType p;
for(i=0; i<E; ++i)//趟次
for(j=0;j<E-i-1; ++j)//没趟比较次数
if(ed[j].weight >= ed[j+1].weight) { p = ed[j+1];
ed[j+1] = ed[j];
ed[j] = p;
}
}
//最小生成树代码实现主体部分
void Kruskal(EdgeGraph G)
{
int parent[MaxVertex];
int i;
int num;
int vex1;
int vex2;
for (i=0; i<G.vertexNum; i++)
{
parent[i] = -1;
}
for (num = 0,i = 0; i<G.edgeNum; i++) {
vex1 = FindRoot(parent,G.edge[i].from);
vex2 = FindRoot(parent,G.edge[i].to);
if (vex1 != vex2)
{
cout << "(" <<G.vertex[ G.edge[i].from] <<" "<< G.vertex[G.edge[i].to] << ")" << endl; //输出生成的边
parent[vex2] = vex1 ;
num++;
if (num == G.vertexNum-1)
{
return ;
}
}
}
}
//求顶点的双亲一直到根
int FindRoot(int parent[],int v)
{
while (parent[v] != -1)
{
if (parent[v] > -1)
{
v = parent[v];
}
}
return v;
}
//主函数
int main(int argc ,char *argv[])
{
EdgeGraph eg;
char x,y;
cout <<"﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊﹊"<<endl;
cout <<"﹡﹡请输入图的顶点数,和边数: "<<endl; cout <<"→ ";
cin >> eg.vertexNum >> eg.edgeNum;
cout <<"请输入各顶点的值: "<<endl; cout <<"→ ";
for (int j =0; j<eg.vertexNum; j++)
{
cin >>eg.vertex[j];
}
cout <<"请分别输入"<<eg.edgeNum<<"条边各边的起点,终点,及其权值: "<<endl;
for (int i=0;i<eg.edgeNum;i++)
{
cout <<"→ ";
cin >>x>>y>>eg.edge[i].weight;
for(int m= 0;m<eg.vertexNum;m++)
{
if(eg.vertex[m]==x)
{
eg.edge[i].from= m;
}
if(eg.vertex[m]==y)
{
eg.edge[i].to= m;
}
}
}
Sort(eg.edge,eg.edgeNum);
cout <<"最小生成树为:"<<endl;
Kruskal(eg);
return 0;
}
程序运行示例:
[实验总结]
此次的设计是自己对已学知识的一个总结和升华,我在此过程中不但应用了所学的知识,而且还结合数据结构等书籍,以完成设计的需要,在设计的过程中我深深体会到,为了实现一个模块的代码、为了一个设计的实现思想、经常绞尽脑汁来达到设计所要达到目的的艰辛,而且通过这次的实验,我也发现了自己还有很多地方有待提高,平时基本知识掌握不牢,导致设计实验过程中走了很多弯路,再有还是不够细心,在此次实验过程中,我曾为了一个误写的变量,调试了三个小时。
这不得不说是一个小小的教训。
由于我的能力所限,所以设计过程中难免有缺点和不足的地方,代码健壮性较差,还有很大的提升空间,我一定会再次改进。