【最小生成树】kruskal算法C语言实现

系列文章【最小生成树】Prim算法C语言实现

kruskal(克鲁斯卡尔)算法

时间复杂度:O(NlogN)(N为边数)
kruskal算法又称“加边法”,用于边数较少的稀疏图
方法:每次找图中权值最小的边(此算法中对于权值的排序运用了快速排序的方法),将边连接的两个顶点加入最小生成树集合中
注意:相同权值任选其中一个即可,但是不允许出现闭合回路的情况(此处运用了并查集的思想)。

这里推荐一个B站的动画介绍的两种最小生成树的原理,建议先看完动画再看代码:
注:动画大概8min

最小生成树(Kruskal(克鲁斯卡尔)和Prim(普里姆))算法动画演示

C语言代码实现

#include <stdio.h>
#define MAXE 100
#define MAXV 100
typedef struct{
	int vex1;                     //边的起始顶点
	int vex2;                      //边的终止顶点
	int weight;                    //边的权值
}Edge;
void kruskal(Edge E[],int n,int e)
{ 
	int i,j,m1,m2,sn1,sn2,k,sum=0;
	int vset[n+1];
	for(i=1;i<=n;i++)        //初始化辅助数组
		vset[i]=i;
	k=1;//表示当前构造最小生成树的第k条边,初值为1
  	j=0;//E中边的下标,初值为0
   while(k<e)//生成的边数小于e时继续循环
   {
       m1=E[j].vex1;
       m2=E[j].vex2;//取一条边的两个邻接点
       sn1=vset[m1];
       sn2=vset[m2];                           
	       //分别得到两个顶点所属的集合编号
	    if(sn1!=sn2)//两顶点分属于不同的集合,该边是最小生成树的一条边
	    {//防止出现闭合回路 
			printf("V%d-V%d=%d\n",m1,m2,E[j].weight);
			sum+=E[j].weight;
			k++;                //生成边数增加 
			if(k>=n)//没有边或者找到的边>顶点数减1都退出循环
				break;
			for(i=1;i<=n;i++)    //两个集合统一编号
				if (vset[i]==sn2)  //集合编号为sn2的改为sn1
					vset[i]=sn1;
	    }
     j++;                  //无论上一条边是否被收入最下生成树,都要扫描下一条边
                           //k++与j++的位置不同,k++在循环内部(只有满足条件才能被收入最小生成树),j++在循环外部
   }
    printf("the lowest weight=%d\n",sum);
}
void swap(Edge arr[],int low,int high)
{
    Edge temp;
    temp = arr[low];
    arr[low] = arr[high];
    arr[high] = temp;
    
}
int fun(Edge arr[],int low,int high)
 {
 	int key;
 	Edge lowx;
 	lowx=arr[low];
 	key=arr[low].weight;
 	while(low<high)
 	{
 		while(low<high && arr[high].weight>=key)
 			high--;
 		if(low<high)
           swap(arr,low,high);
 			//arr[low++]=arr[high];

 		while(low<high && arr[low].weight<=key)
 			low++;
 		if(low<high)
           swap(arr,low,high);
 			//arr[high--]=arr[low];
	 }
	 arr[low]=lowx;
	 return low;
  } 
void quick_sort(Edge arr[],int start,int end)
{
	int pos;
	if(start<end)
	{
	pos=fun(arr,start,end);
	quick_sort(arr,start,pos-1);
	quick_sort(arr,pos+1,end);
	}
}
void gen(Edge E[],int vertex,int edge)
{
    for(int i=0;i<edge;i++)
		scanf("%d%d%d",&E[i].vex1,&E[i].vex2,&E[i].weight);
}
int main()
{
	Edge E[MAXE];
	int vertex,edge;
    //freopen("1.txt","r",stdin);//文件输入
	printf("please intput the vertexs and edges:\n");
	scanf("%d%d",&vertex,&edge);
    gen(E,vertex,edge);
	quick_sort(E,0,edge-1);
	kruskal(E,vertex,edge);
}

输入:

6 9
1 4 5
3 4 5
1 3 1
4 6 2
2 5 3
2 3 5
3 5 6
1 2 6
3 6 4

输出:

V1-V3=1
V4-V6=2
V2-V5=3
V3-V6=4
V2-V3=5
the lowest weight=15
  • 21
    点赞
  • 122
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
以下是C语言实现Kruskal算法求解最小生成树的完整代码,其中包括数据结构的定义和算法实现: ```c #include <stdio.h> #include <stdlib.h> #define MAX_VERTEX_NUM 100 // 最大顶点数 #define MAX_EDGE_NUM 100 // 最大边数 // 定义边的结构体 typedef struct { int u; // 边的起点 int v; // 边的终点 int w; // 边的权值 } Edge; // 定义并查集结构体 typedef struct { int parent[MAX_VERTEX_NUM]; // 父节点数组 int rank[MAX_VERTEX_NUM]; // 秩数组 } UnionFindSet; // 初始化并查集 void MakeSet(UnionFindSet *S, int n) { for (int i = 0; i < n; i++) { S->parent[i] = i; S->rank[i] = 0; } } // 查找节点所在集合的根节点 int Find(UnionFindSet *S, int x) { if (S->parent[x] != x) { S->parent[x] = Find(S, S->parent[x]); } return S->parent[x]; } // 合并两个集合 void Union(UnionFindSet *S, int x, int y) { int root1 = Find(S, x); int root2 = Find(S, y); if (root1 == root2) { return; } if (S->rank[root1] > S->rank[root2]) { S->parent[root2] = root1; } else if (S->rank[root1] < S->rank[root2]) { S->parent[root1] = root2; } else { S->parent[root2] = root1; S->rank[root1]++; } } // Kruskal算法求解最小生成树 void Kruskal(Edge *E, int n, int m) { UnionFindSet S; MakeSet(&S, n); int count = 0; for (int i = 0; i < m; i++) { int u = E[i].u; int v = E[i].v; int w = E[i].w; if (Find(&S, u) != Find(&S, v)) { printf("(%d, %d) %d\n", u, v, w); Union(&S, u, v); count++; if (count == n - 1) { break; } } } } int main() { int n, m; Edge E[MAX_EDGE_NUM]; printf("请输入顶点数和边数:"); scanf("%d%d", &n, &m); printf("请输入每条边的起点、终点和权值:\n"); for (int i = 0; i < m; i++) { scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].w); } Kruskal(E, n, m); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值