数据结构(C):最小生成树Prim算法和Kruscal算法

1.Prim算法(选点)

思想:①设置一个点集合V,表示最小生成树中已经确定的点的集合;设置一个边集合U,表示最小生成树中已经确定的边的集合;(U和V是对应起来的),
②在V中点,和剩余点中找权值最小的边,将边并入U,对应的另一个端点并入V.(注意在找边的时候避免和原来的边产生环)
③重复步骤②,直到V中的顶点为所有的顶点,U中的边则为最小生成树上的边。
时间复杂度O(n^2),适用于点少的图
例如:下图
在这里插入图片描述
先找{1}和{2,3,4,5,6}中权最小的边为(1,3)
再找{1,3}和{2,4,5,6}中权最小的边为(3,6)
······
代码:

#include<stdio.h>
#define MAX_VERTEX_NUM 100
#define MAX 1e10
typedef struct{
	int vexs[MAX_VERTEX_NUM];
	int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
	int vexnum,arcnum;
}MGraph;
struct{
	int adjvex;
	int lowcost;
}closedge[MAX_VERTEX_NUM];
bool inU[MAX_VERTEX_NUM]; //如果值为1表示vex【i】顶点在U中
int LocateVex(MGraph G,int v){//返回顶点在图中的位置
	for(int i=0;i<G.vexnum;i++){
		if(G.vexs[i]==v)return i;
	}
}
void CreateUDN(MGraph &G){//构造无向网的邻接矩阵
	int i,j,k,v1,v2,weight;
	printf("分别输入顶点个数和边的个数:\n");
	scanf("%d%d",&G.vexnum,&G.arcnum);
	printf("输入各个顶点:\n");
	for(i=0;i<G.vexnum;i++)
	scanf("%d",&G.vexs[i]);

	for(i=0;i<G.vexnum;i++)
	 for(j=0;j<G.vexnum;j++)
	  G.arcs[i][j]=MAX;//初始化图,如果两点不通则把值赋为MAX

	printf("分别输入各条边的两个顶点和边的权值:\n");
	for(k=0;k<G.arcnum;k++){
		scanf("%d%d%d",&v1,&v2,&weight);
		i=LocateVex(G,v1);j=LocateVex(G,v2);
		G.arcs[i][j]=weight;
		G.arcs[j][i]=weight;
	}
}
int minmum(MGraph G){//返回low_cost最小的
	int min_cost=MAX;
	int temp;
	for(int i=0;i<G.vexnum;i++)
		if(closedge[i].lowcost<min_cost&&closedge[i].lowcost!=0&&inU[LocateVex(G,closedge[i].adjvex)]){
			min_cost=closedge[i].lowcost;temp=i;
		}
	return temp;
}
void MiniSpanTree_PRIM(MGraph G,int u){//找到最小生成树的边并输出
	int i,j,k;
	k=LocateVex(G,u);
	inU[k]=1;// 表示把vex[k]加入到U集合中
	for(j=0;j<G.vexnum;j++)//初始化,即有一个顶点时U集 到V-U集的最小权值
	if(j!=k){
		closedge[j].adjvex=u;
		closedge[j].lowcost=G.arcs[k][j];
	}
	closedge[k].lowcost=0;
	printf("最小生成树的边为:\n");
	for(i=1;i<G.vexnum;i++){//U集总共还要加入vexnum-1个顶点
		k=minmum(G);//当前U到 V-U集最小的一个权值为closedge[k].lowcost
		inU[k]=1; //将vex[k]加入U集合
	printf("%d %d\n",closedge[k].adjvex,G.vexs[k]);
	closedge[k].lowcost=0;
	for(j=0;j<G.vexnum;j++){//当有新的顶点加入U集时,每个顶点到V-U集的lowcost都要重新赋值
		if(G.arcs[k][j]<closedge[j].lowcost)//如果新的顶点到j的权值小与未加入新顶点之前的lowcost,则重新赋值
		{closedge[j].adjvex=G.vexs[k];
		closedge[j].lowcost=G.arcs[k][j];}
	}
	}
}
int main(){
	MGraph G;
	CreateUDN(G);
	int u;//最小生成树的起点为u
	printf("输入生成树的出发点:\n");
	scanf("%d",&u);
	MiniSpanTree_PRIM(G,u);
	return 0;
}

2.Kruskal(选边)

思想:
从原图中依次选择权值最小且不与已选的边构成环的边,直到所有顶点都连通
时间复杂度:O( e * loge )
适合于求边稀疏的网的最小生成树
代码

/* 最小生成树的Kruskal算法 */
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
#define MaxSize 20
 
typedef char VertexType;
 
typedef struct Graph {		//定义图的邻接矩阵表示法结构
	VertexType ver[MaxSize+1];
	int edg[MaxSize][MaxSize];
}Graph;
 
typedef struct gEdge {		//定义一个边集结构,用来存储图的所有边信息
	int begin;
	int end;
	int weight;
}gEdge;
 
void CreateGraph( Graph *g )		//邻接矩阵法图的生成函数
{
	int i = 0;
	int j = 0;
	int VertexNum;
	VertexType Ver;
 
	printf("请输入图的顶点:\n");
	while( '\n' != (Ver=getchar()) ) {
		g->ver[i++] = Ver;
	}
	g->ver[i] = '\0';
 
	VertexNum = strlen(g->ver);
	printf("请输入相应的的邻接矩阵:\n");
	for( i=0; i<VertexNum; i++ )
		for( j=0; j<VertexNum; j++ ) 
			scanf("%d", &g->edg[i][j]);
}
 
void PrintGraph( Graph g )		//打印图的结点标识符和邻接矩阵
{
	int i, j;
	int VertexNum = strlen(g.ver);
	printf("图的顶点为:\n");
	for( i=0; i<VertexNum; i++ )
		printf("%c ", g.ver[i]);
	printf("\n");
 
	printf("图的邻接矩阵为:\n");
	for( i=0; i<VertexNum; i++ ) {
		for( j=0; j<VertexNum; j++ )
			printf("%d ", g.edg[i][j]);
		printf("\n");
	}
}
 
int CalVerNum( Graph g )		//求图的顶点数
{
	return strlen(g.ver);
}
 
int CalEdgNum( Graph g )		//获取图的边数
{
	Graph p = g;
	int count = 0;
	int VertexNum = CalVerNum( p );
	for( int i=0; i<VertexNum; i++ )
		for( int j=i; j<VertexNum; j++ )		//邻接矩阵对称,计算上三角元素和即可
			if( 0 != p.edg[i][j] )
				count++;
	return count;
}
 
gEdge *CreateEdges( Graph g )					//生成图的排序过的边集数组
{
	int i, j;
	int k = 0;
	int EdgNum = CalEdgNum( g );
	int VertexNum = CalVerNum( g );
	gEdge temp;
	gEdge *p = (gEdge *)malloc(sizeof(gEdge)*EdgNum);	
 
	for( i=0; i<VertexNum; i++ )				//边集数组初始化,同样只考虑上三角矩阵
		for( j=i; j<VertexNum; j++ )
			if( 0 != g.edg[i][j] ) {
				p[k].begin = i;
				p[k].end = j;
				p[k].weight = g.edg[i][j];
				k++;
			}
	
	for( i=0; i<EdgNum-1; i++ )		        	//对边集数组进行选择排序
		for( j=i+1; j<EdgNum; j++ )
			if( p[i].weight > p[j].weight ) {
				temp = p[i];
				p[i] = p[j];
				p[j] = temp;
			}
 
	return p;
}
 
//Kruskal算法生成MST
void Kruskal( Graph g )
{
	int VertexNum = CalVerNum( g );
	int EdgNum = CalEdgNum( g );
	gEdge *p = CreateEdges( g );
	int *index = (int *)malloc(sizeof(int)*VertexNum);		    
	//index数组,其元素为连通分量的编号,index[i] = index[j] 表示编号为i 和 j的顶点在同一个连通分量中,反之则不在同一个连通分量
	int *MSTEdge = (int *)malloc(sizeof(int)*VertexNum-1);		//MSTEdge数组,用来存储已确定的MST的边的编号,共VertexNum-1条边
	int k= 0;
	int WeightSum = 0;
	int IndexBegin, IndexEnd;
 
	for(int i=0; i<VertexNum; i++ )		//初始化所有index = -1
		index[i] = -1;
 
	for( i=0; i<VertexNum-1; i++ ) {
		for(int j=0; j<EdgNum; j++ ) {
			if( !(index[p[j].begin]>=0 && index[p[j].end]>=0 && index[p[j].begin]==index[p[j].end]) ) {  //找到当前还没加入到同一个连通分量且权值最下的边
				MSTEdge[i] = j;		//将其加入到MST中去
 
				if( (-1 == index[p[j].begin]) && (-1 == index[p[j].end]) )			//该边的两个顶点都没加入过任何一个连通分量
					index[p[j].begin] = index[p[j].end] = i;	
 
				else if( (-1 == index[p[j].begin]) && (index[p[j].end] >= 0) ) {	//该边的尾end已在一个连通分量,头begin未加入过任何连通分量
					index[p[j].begin] = i;
					IndexEnd = index[p[j].end];
					for(int n=0; n<VertexNum; n++ )
						if( index[n] == IndexEnd )
							index[n] = i;
				}
 
				else if( (-1 == index[p[j].end]) && (index[p[j].begin] >= 0) ) {	//该边的头begin已在一个连通分量,尾end未加入过任何连通分量
					index[p[j].end] = i;
					IndexBegin = index[p[j].begin];
					for(int n=0; n<VertexNum; n++ )
						if( index[n] == IndexBegin )
							index[n] = i;
				}
 
				else {
					IndexEnd = index[p[j].end];
					IndexBegin = index[p[j].begin];
					for(int n=0; n<VertexNum; n++ )								//该边的两个顶点都已经存在于两个不同的连通分量中
						if( index[n] == IndexBegin || index[n] == IndexEnd )
							index[n] = i;										//将该连通分量编号全部修改为相同的值
				}
				break;
			}
		}
	}
 
	printf("MST的边为:\n");				//输出MST的边
	for( i=0; i<VertexNum-1; i++ ) {
		printf("%c--%c\n", g.ver [p[MSTEdge[i]].begin] , g.ver [p[MSTEdge[i]].end] );
		WeightSum+=p[MSTEdge[i]].weight;
	}
	printf("MST的权值为:%d\n", WeightSum);		//输出MST的权值
}
 
int main()
{
	Graph g;
	CreateGraph( &g );
	PrintGraph( g );
	Kruskal( g );
	return 0;
}

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值