最小生成树算法例题


一、问题描述

  对于下图,试分别采用Prim和Kruskal算法求出其最小生成树。

二、实现思路

  最小生成树是由n个顶点,n-1条边,将一个连通图连接起来,且使权值最小的结构。最小生成树可以用Prim算法或Kruskal算法求出。Prim算法又称“加点法”,用于边数较多的带权无向连通图。其方法主要是每次找与之连线权值最小的顶点,将该点加入最小生成树集合中,但是不允许出现闭合回路的情况。Kruskal算法又称“加边法”,用于边数较少的稀疏图,其方法主要是每次找图中权值最小的边,将边连接的两个顶点加入最小生成树集合中,但是不允许出现闭合回路的情况。
  Prim()算法中有两重for循环,所以时问复杂度为O(n2),其中n为图的顶点个数。Prim()算法的执行时间与图中的边数e无关,所以它特别适合用稠密图求最小生成树。
  在Kruakal算法中,如果给定的带权连通图G有n个顶点、e条边。在上述算法中,对边集E采用直接插入排序的时间复杂度为O(e2),while循环是在e条边中选取(n-1)条边,而其中的for循环执行n次。因此while循环的时间复杂度为O(n2+ e2)。则Kruakal算法构造最小生成树的时间复杂度为O(e2)。

三、解题代码

#include <stdio.h>
#include <malloc.h>
#define MaxSize 100
#define INF 32767				//定义∞
#define	MAXV 100				//最大顶点个数
typedef char InfoType;
typedef struct
{
	int u;			//边的起始顶点
	int v;			//边的终止顶点
	int w;			//边的权值
} Edge;


//以下定义邻接矩阵类型
typedef struct
{
	int no;						//顶点编号
	InfoType info;				//顶点其他信息
} VertexType;					//顶点类型
typedef struct
{
	int edges[MAXV][MAXV];		//邻接矩阵数组
	int n, e;					//顶点数,边数
	VertexType vexs[MAXV];		//存放顶点信息
} MatGraph;						//完整的图邻接矩阵类型

void CreateMat(MatGraph &g, int A[MAXV][MAXV], int n, int e) //创建图的邻接矩阵
{
	int i, j;
	g.n = n; g.e = e;
	for (i = 0; i < g.n; i++)
		for (j = 0; j < g.n; j++)
			g.edges[i][j] = A[i][j];
}
void DispMat(MatGraph g)	//输出邻接矩阵g
{
	int i, j;
	for (i = 0; i < g.n; i++)
	{
		for (j = 0; j < g.n; j++)
			if (g.edges[i][j] != INF)
				printf("%4d", g.edges[i][j]);
			else
				printf("%4s", "∞");
		printf("\n");
	}
}

void InsertSort(Edge E[], int n) //对E[0..n-1]按递增有序进行直接插入排序
{
	int i, j;
	Edge temp;
	for (i = 1; i < n; i++)
	{
		temp = E[i];
		j = i - 1;				//从右向左在有序区E[0..i-1]中找E[i]的插入位置
		while (j >= 0 && temp.w < E[j].w)
		{
			E[j + 1] = E[j];	//将关键字大于E[i].w的记录后移
			j--;
		}
		E[j + 1] = temp;		//在j+1处插入E[i] 
	}
}
void Kruskal(MatGraph g)
{
	int i, j, u1, v1, sn1, sn2, k;
	int vset[MAXV];
	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++;
			}
		}
	InsertSort(E, g.e);				//采用直接插入排序对E数组按权值递增排序
	for (i = 0; i < g.n; i++) 			//初始化辅助数组
		vset[i] = i;
	k = 1;                 			//k表示当前构造生成树的第几条边,初值为1
	j = 0;                 			//E中边的下标,初值为0
	while (k < g.n)       			//生成的边数小于n时循环
	{
		u1 = E[j].u; v1 = E[j].v;        //取一条边的头尾顶点
		sn1 = vset[u1];
		sn2 = vset[v1]; 				//分别得到两个顶点所属的集合编号
		if (sn1 != sn2)     	  		//两顶点属于不同的集合,该边是最小生成树的一条边
		{
			printf(" 边(%d,%d)权为:%d\n", u1, v1, E[j].w);
			k++;                    //生成边数增1
			for (i = 0; i < g.n; i++)     //两个集合统一编号
				if (vset[i] == sn2)  	//集合编号为sn2的改为sn1
					vset[i] = sn1;
		}
		j++;   						//扫描下一条边
	}
}

void Prim(MatGraph g, int v)
{
	int lowcost[MAXV];			//顶点i是否在U中
	int min;
	int closest[MAXV], i, j, k;
	for (i = 0; i < g.n; i++)          //给lowcost[]和closest[]置初值
	{
		lowcost[i] = g.edges[v][i];
		closest[i] = v;
	}
	for (i = 1; i < g.n; i++)          //找出n-1个顶点
	{
		min = INF;
		for (j = 0; j < g.n; j++)       //在(V-U)中找出离U最近的顶点k
			if (lowcost[j] != 0 && lowcost[j] < min)
			{
				min = lowcost[j];
				k = j;			//k记录最近顶点的编号
			}
		printf(" 边(%d,%d)权为:%d\n", closest[k], k, min);
		lowcost[k] = 0;         	//标记k已经加入U
		for (j = 0; j < g.n; j++)   	//修改数组lowcost和closest
			if (g.edges[k][j] != 0 && g.edges[k][j] < lowcost[j])
			{
				lowcost[j] = g.edges[k][j];
				closest[j] = k;
			}
	}
}

int main()
{
	MatGraph g;
	int A[MAXV][MAXV] = {  //报告中的所求图的邻接矩阵
		{0,1,3,4},
		{1,0,2,INF},
		{3,2,0,5},
		{4,INF,5,0} };
	int n = 4 , e = 5;
	CreateMat(g, A, n, e);			//建立邻接矩阵
	printf("图G的邻接矩阵:\n");
	DispMat(g);					//输出邻接矩阵
	int v = 0;
	printf("Prim算法结果(起始点为%d)\n", v);
	Prim(g, v);
	printf("Kruskal算法结果\n");
	Kruskal(g);	
	return 1;
}

四、运行结果

在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值