【C语言】实现求最小生成树的Kruskal算法

题目描述

已知有权无向图G,利用克鲁斯卡尔算法求出该图的最小生成树。

输入格式:

第一行输入两个正整数n和m(空格间隔), 分别表示图G的顶点总数和边的总数。 第二行连续输入n个字母,分别表示n个顶点的信息。

第三行连续输入m条边的信息,每条边的输入格式为(v1,v2,w),表示一条关联顶点v1和v2的边,其权值为w。

输出格式:

按边上权值由小到大的顺序依次输出各个边。

输出边的时,若该边被选中,则在该边信息之后输出1,否则输出0。

例如: (v1,v2,w,1) 表示与顶点v1和v2相关联的边,权值为w,被选中。 (v1,v2,w,0)
表示与顶点v1和v2相关联的边,权值为w,未被选中。

输入样例:

6 10 ABCDEF
(A,B,3)(A,D,4)(A,F,10)(B,C,15)(B,D,1)(B,F,8)(C,D,17)(D,E,6)(D,F,13)(E,F,2)
结尾无空行

输出样例:

(B,D,1,1)(E,F,2,1)(A,B,3,1)(A,D,4,0)(D,E,6,1)(B,F,8,0)(A,F,10,0)(D,F,13,0)(B,C,15,1)(C,D,17,0)
结尾无空行

思想

我们可以把图里的每一个顶点看成是一个森林,每一个顶点就是一棵树。

算法思想:
kruskal算法即将每一条边按权值从小到大的顺序排序,选择权值最小的边加入到最小生成树里面
每次加入到最小生成树里面要判断是否会造成环形回路(关键)

如何判断?

判断两个顶点是否有相同的根节点

  • 如果有,就不进行操作

  • 如果没有,就将这两个顶点添加到生成树集合里面(parent[i]=j:i的双亲为j ---- 双亲表示法)

代码实现

#include<stdio.h>
#include<malloc.h>
#define MAX 32767
typedef struct Matrix//定义结构体 
{
	int arcs[20][20];//结构体大小 
	int poinum;//顶点的数目 
	int edenum;//边的数目 
	char vex[20];//顶点的信息 

}Matrix;

typedef struct edge //定义边的结构体 
{
	int begin;//纪录边的两个结点 
	int end;
	int weight;
	int isfind;//纪录是否被使用到 
}Edge;

Edge edge[20];//定义边集数组 

int Location(Matrix *M,char a)//查找函数 
{
	for(int i=0;i<M->poinum;i++){
		if(M->vex[i] == a)
			return i;
		
	}
	return 0;
}

//查找v所在树的根节点 
int find(int *parent, int v)
{
    while (parent[v] > 0)
    {
        v = parent[v];//寻找双亲结点直至根节点 
    }
    return v;
}

void Sort(Matrix *M,int k)
{
	//使用冒泡排序 
	Edge tmp;
	int i,j;
	for(i=0;i<k-1;i++)
	{
		for(j=i+1;j<k;j++)
		{
			if(edge[i].weight>edge[j].weight)
			{
				tmp = edge[i];
				edge[i] = edge[j];
				edge[j] = tmp;
			}
		}
	}
}


void kruskal(Matrix *M)
{
	int num;
	int parent[100] = {0};//纪录能否生成环路 - 数组纪录双亲结点的位置 
	int i,j,k=0;
	int v1,v2;
	//将邻接矩阵转换为边集数组 
	for(i=0;i<M->poinum-1;i++)
	{
		for(j=i+1;j<M->poinum;j++)
		{
			if(M->arcs[i][j]!=MAX)
			{
				edge[k].begin = i;//将行下标放到begin里面 
				edge[k].end = j;//将列下标放到end里面 
				edge[k].weight = M->arcs[i][j];//权值赋 
				edge[k].isfind = 0;//判断边是否被使用的变量初始化为0 
				k++;//边集数组的下标 
			}
		}
	}
	Sort(M,k);//按权值升序排序 
	
	for(i=0;i<M->edenum;i++)
	{
		v1 = find(parent,edge[i].begin);//查找begin的根节点 
		v2 = find(parent,edge[i].end);//查找end的根节点 
		
		if(v1!=v2)//判断根节点是否相同 
		{
			parent[v1] = v2;//v1的双亲结点为v2 - 标志着v2顶点加入到生成树集合里面 
			edge[i].isfind = 1;//改变是否被使用 - 改为已使用 
			num++;//变量num纪录加入生成树边的数目 
		}
		//优化:提前退出 
		printf("(%c,%c,%d,%d)",M->vex[edge[i].begin], M->vex[edge[i].end], edge[i].weight, edge[i].isfind);
		if(num==M->poinum-1)return;//如果加入生成树的边数等于顶点数目-1,可以退出不再进行判断 
	}
}

int main()
{
	int i,j,k,weight;
	Matrix *M = (Matrix*)malloc(sizeof(Matrix));//申请邻接矩阵空间 
	char vex1,vex2;
	scanf("%d%d",&M->poinum,&M->edenum);
	getchar();
	for(i=0;i<M->poinum;i++){
		for(j=0;j<M->poinum;j++){
			M->arcs[i][j] = MAX;//邻接矩阵初始化 
		}
	} 
	for(i=0;i<M->poinum;i++)
	{
		scanf("%c",&M->vex[i]);
	}
	getchar();
	
	for(i=0;i<M->edenum;i++)
	{
		scanf("(%c,%c,%d)",&vex1,&vex2,&weight);
		j = Location(M,vex1);//查找所在行下标 
		k = Location(M,vex2);//查找所在列下标 
		M->arcs[j][k] = weight;//保存权值 
		M->arcs[k][j] = M->arcs[j][k];//无向图 - 对称的 
	} 
	
	kruskal(M); 
}
  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是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; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值