基于C语言的克鲁斯卡尔算法

Posted Mr.zhou_Zxy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于C语言的克鲁斯卡尔算法相关的知识,希望对你有一定的参考价值。

/*
克鲁斯卡尔算法:
1.克鲁斯卡尔算法是一种按照权值的递增次序选择合适的边来构造最小生成树的方法。
2.G=(V,E)是一个具有n个顶点e条边的带权连通无向图,t=(e,te)是g的最小生成树。
3.通过并查集判断选取的边与树中已经保留的边是否冲突
4.n元素的分离集合树,其高度最高为log2n

*/

#include<stdio.h>
#define INF 32767
#define MAXV 20
#define MAXL 20
int MaxSize = 100;//设置最大的容量 
typedef struct node
{
	int data;//节点对应顶点编号 
	int rank;//节点对应秩(深度) 
	int parent;//节点对应双亲下标 
}UFSTree;//并查集数的节点类型 
typedef struct
{
	int u;//边的起始顶点 
	int v;//边的终止节点 
	int w;//边的权值 
 } Edge;
typedef struct
{	int no;						//顶点编号
	char data[MAXL];			//顶点其他信息
} VertexType;					//顶点类型
typedef struct
{	int edges[MAXV][MAXV];		//邻接矩阵的边数组
	int n,e;					//顶点数,边数
	VertexType vexs[MAXV];		//存放顶点信息
} MGraph;						//完整的图邻接矩阵类型
void CreateMat(MGraph &g,int A[][MAXV],int n,int e)	//建立图的邻接矩阵
{	int i,j;
	g.n=n; g.e=e;
	for (i=0;i<n;i++)
		for (j=0;j<n;j++)
			g.edges[i][j]=A[i][j];
}
void DispMat(MGraph g)			//输出图的邻接矩阵
{	int i,j;
	printf("  n=%d,e=%d\\n",g.n,g.e);
	for (i=0;i<g.n;i++)
	{	for (j=0;j<g.n;j++)
			if (g.edges[i][j]==INF)
				printf("%4s","∞");
			else
				printf("%4d",g.edges[i][j]);
		printf("\\n");
	}
}
void MAKE_SET(UFSTree t[],int n)//初始化 
{
	int i;
	for(i=0;i<n;i++)//顶点编号为0-n-1 
	{
		t[i].rank=0;//秩(深度)初始化 
		t[i].parent=i;//双亲初始化指向自己 
	}
 } 
int FIND_SET(UFSTree t[],int x)//在x所在子树中查找编号 
 {
 	if(x!=t[x].parent)
 		return(FIND_SET(t,t[x].parent));
 	else
 		return(x);
 }
void UNION(UFSTree t[],int x,int y)//将x和y所在子树合并 
 {
 	x=FIND_SET(t,x);//查找编号 
 	y=FIND_SET(t,y);
 	if(t[x].rank>t[y].rank)//y节点的深度小于x节点的深度 
 		t[y].parent=x;//将y连接到x上,x作为y的节点
		  
 	else
 	{
 		t[x].parent=y;
 		if(t[x].rank==t[y].rank)
 			t[y].rank++;
	 }
 }
void sift(Edge R[],int low,int high)
{	int i=low,j=2*i;						//R[j]是R[i]的左孩子
	Edge tmp=R[i];
	while (j<=high)
	{	if (j<high && R[j].w<R[j+1].w) 	//若右孩子较大,把j指向右孩子
			j++;
		if (tmp.w<R[j].w) 
		{	R[i]=R[j];						//将R[j]调整到双亲节点位置上
			i=j;							//修改i和j值,以便继续向下筛选
			j=2*i;
		}
		else break;							//筛选结束
	}
	R[i]=tmp;								//被筛选节点的值放入最终位置
}
void HeapSort(Edge R[],int n)
{	int i;
	Edge tmp;
	for (i=n/2;i>=1;i--)	//循环建立初始堆
		sift(R,i,n); 
	for (i=n;i>=2;i--)		//进行n-1趟完成推排序,每一趟堆排序的元素个数减1
	{	tmp=R[1];			//将最后一个元素同当前区间内R[1]对换
		R[1]=R[i];
		R[i]=tmp;
		sift(R,1,i-1);		//筛选R[1]节点,得到i-1个节点的堆
	}
}
void Kruskal(MGraph g)
{	
	int i,j,k,u1,v1,sn1,sn2;
	UFSTree t[MaxSize];
	Edge E[MaxSize];
	k=0;//e数组的下标从0开始
	for(i=0;i<g.n;i++)//由g下三角部分产生的边集E
		for(j=0;j<i;j++)
			if(g.edges[i][j]!=0&&g.edges[i][j]!=INF)
			{
				E[k].u=i;E[k].v=j;E[k].w=g.edges[i][j];
				k++;
			}
	HeapSort(E,g.e);//采取堆排序对E数组按权值递增排序
	MAKE_SET(t,g.n);//初始化并查集t	
	k=1;//k表示当前构造树的第几条边,初值为1
	j=0;//E中边的下标,初值为0
	while(k<g.n)
	{
		u1=E[j].u;
		v1=E[j].v;//取一条边的头尾顶点编号U1和V1
		sn1=FIND_SET(t,u1);
		sn2=FIND_SET(t,v1);//分别得到两个顶点所属的集合编号
		if(sn1!=sn2)//添加该边不会构成回路,将该边作为最小生成树的一条边
		{
			printf("(%d,%d):%d\\n",u1,v1,E[j].w);
			k++;//生成边数增1
			UNION(t,u1,v1);//扫描下一条边
		}
		j++;	
	}	
 } 
int main()
{
	MGraph g;
	int A[][MAXV]={
					{0,4,6,6,INF,INF,INF},
					{4,0,1,INF,7,INF,INF},
					{6,1,0,2,6,4,INF},
					{6,INF,2,0,INF,1,6},
					{INF,7,6,INF,0,1,6},
					{INF,INF,4,5,1,0,8},
					{INF,INF,INF,INF,6,8,0}
					};
	int n=7,e=12;
	CreateMat(g,A,n,e);//创建邻接矩阵 
	printf("图的邻接矩阵"); 
	DispMat(g);//输出邻接矩阵 
	printf("Kruskal算法结果\\n");
	Kruskal(g); //调用prim算法 
}	


以上是关于基于C语言的克鲁斯卡尔算法的主要内容,如果未能解决你的问题,请参考以下文章

大话数据结构C语言46 最小生成树(克鲁斯卡尔算法)

C语言 克鲁斯卡尔算法怎么判断是不是构造成回路?求大手解答

克鲁斯卡尔算法

Kruskal算法 - C语言详解

Kruskal算法详解

克鲁斯卡尔算法