【数据结构】Kruskal和Prim算法生成最小生成树的技术实现及比较

引言:

在图论中,最小生成树是一个连通图的生成树,它的所有边的权值之和最小。Kruskal和Prim算法是两种常用的生成最小生成树的算法,本文将对它们进行比较和介绍。

技术实现:

一、Kruskal算法:

Kruskal算法是一种基于贪心策略的算法,它通过不断选择边来构建最小生成树。具体步骤如下: 

        1.定义结构体,定义边集数组。

#include
#include


typedef char VertexType;
typedef int EdgeType;
#define MAXEDGE 15
#define MAXVAX 9
#define INFINITY 65535

typedef struct {
	VertexType vex[MAXVAX];//顶点表
	EdgeType arc[MAXEDGE][MAXEDGE];//邻接矩阵
	int numVertexes, numEdges;//顶点数和边数

}MGraph;


//对边集数组Edge的定义
typedef struct {
	int begin;
	int end;
	int weight;
}Edge;

void MiniSpnTRee_Kruskal(MGraph G);

        2.定义一个Find函数,如果数值大于0,则返回。

int Find(int* parent, int f) {
	while (parent[f] > 0)
		f = parent[f];
	return f;
}

        3.进行Kruskal算法,定义parent数组判断是否形成回路,利用Find函数计算边的两个顶点数量,如果不等,说明没有环路,入parent数组,再输出边的头尾和权值。 

void MiniSpnTRee_Kruskal(MGraph G) {
	int i, n, m;
	Edge edges[MAXEDGE];//定义边集数组
	int parent[MAXVAX];//定义一数组用来判断边与边是否形成环路

	for (i = 0; i < G.numVertexes; i++) {
		parent[i] = 0;//初始化数组为0
	}
	for (i = 0; i < G.numEdges; i++) {//循环每一条边
		n = Find(parent, edges[i].begin);
		m = Find(parent, edges[i].end);
		if (n != m) {//假使n与m不等,说明没有生成与现有生成树形成环路
			parent[n] = m;//将此边结尾顶点放入下标为起点的parent中,表示顶点已经在生成树中
			printf("(%d,%d) %d", edges[i].begin, edges[i].end, edges[i].weight);
		}
	}
}

 二、Prim算法

Prim算法也是一种基于贪心策略的算法,它通过不断选择顶点来构建最小生成树。具体步骤如下: 

        1. 定义邻接矩阵。

#include
#include

typedef char VertexType;
typedef int EdgeType;
#define MAXEDGE 15
#define MAXVAX 9
#define INFINITY 65535

typedef struct {
	VertexType vex[MAXVAX];//顶点表
	EdgeType arc[MAXEDGE][MAXEDGE];//邻接矩阵
	int numVertexes, numEdges;//顶点数和边数

}MGraph;

void MiniSpanTree_Prim(MGraph G);

        2.  进行Prim算法。选择0为起始点,将其加入最小生成树,找到与其相连权值最小的边,将其加入生成树,重复该过程,直到树中有n-1条边。

void MiniSpanTree_Prim(MGraph G) {
	int min, i, j, k;
	int adjvex[MAXVAX];//保存相关顶点下标
	int lowcost[MAXVAX];//保存相关顶点间边的权值
	lowcost[0] = 0;//初始化第一个点,即v0加入生成树

	adjvex[0] = 0;//初始化第一个顶点下标为0
	for (i = 1; i < G.numVertexes; i++) {//循环除下标为0外的全部顶点
		lowcost[i] = G.arc[0][i];//将v0顶点与之有边的权值存入数值
		adjvex[i] = 0;//初始化都为v0的下标
	}
	for (i = 1; i < G.numVertexes; i++) {
		min = INFINITY;//初始化最小边为无穷

		j = 1; k = 0;
		while (j < G.numVertexes) {//循环全部顶点
			if (lowcost[j] != 0 && lowcost[j] < min) {
				min = lowcost[j];//则让当前权值成为最小值
				k = j;//将当前最小值的下标存入k
			}
			j++;
		}
		printf("(%d,%d)", adjvex[k], k);//打印当前顶点边中权值最小边
		lowcost[k] = 0;//将当前顶点权值设置成0,表示已经完成任务
		for (j = 1; j < G.numVertexes; j++) {//循环所有顶点
			if (lowcost[j] != 0 && G.arc[k][j]) {//若下标为k顶点各边权值小于此前顶点未被加入生成树权值
				lowcost[j] = G.arc[k][j];//将较小权值存入lowcost
				adjvex[j] = k;//将下标为k的顶点存入adjvex
			}
		}
	}
}

区别: 

  1. 时间复杂度:Kruskal算法的时间复杂度相对较低,适用于边数较多的情况;而Prim算法的时间复杂度较高,适用于顶点数较多的情况。
  2. 空间复杂度:Kruskal算法需要额外的存储空间来存储并查集,而Prim算法只需要存储最小生成树的顶点集合。
  3. 实现难度:Kruskal算法相对较容易实现,只需要对边进行排序和并查集操作;而Prim算法需要对顶点进行选择和判断相邻边的权值。
  4. 边权值:Kruskal算法适用于边权值可以任意的情况;而Prim算法适用于边权值必须为正数的情况。

结论:

Kruskal算法和Prim算法都是生成最小生成树的常用算法,选择哪种算法要根据具体情况来决定。如果边数较多,可以选择Kruskal算法;如果顶点数较多,可以选择Prim算法。同时,还需要考虑边权值的情况,如果边权值可以任意,则可以选择Kruskal算法。

 

你可能感兴趣的:(数据结构,数据结构,算法)