【数据结构】最小生成树问题

MST性质

假设N={V, {E}}是一个连通网,U是顶点集V的一个非空子集,若(u, v)是一条具有最小权值的边,其中u∈U, v∈V-U,则比存在一棵包含边(u, v)的最小生成树

最小生成树的Prim算法描述

假设集合U里面存放的是已经在最小生成树中的顶点,集合TE存放的是最小生成树的边,算法从U={u0}开始,在所有u∈U,v∈V-U的所有边(u, v)中选一条权值最小的边加入生成树,同时v0加入顶点集合U中,重复上述操作,直至所有顶点都已经加入顶点集合为止

一个鲜活的例子

代码实现

//prim算法的最小生成树//
void minispanTree_prim(MGraph *G) {
	//adjvex表示最小权值边的起始顶点,j表示结束顶点
	int min, i, j, k;//会用到的变量
	int adjvex[MAXVEX];//用来存放边的其中一个顶点
	int lowcost[MAXVEX];//用来存放当前已经连通部分的最小权值(对应于adjvex中的顶点)
	lowcost[0] = 0;//初始化第一个数值为0,表示第一个顶点已经加入最小生成树
	adjvex[0] = 0;//第一个顶点下标
	for (i = 1; i < G->Vertex_num; i++) {
		lowcost[i] = G->arc[0][i];
		adjvex[i] = 0;//一开始最小生成树的顶点只有0
	}
	//初始化完成,接下来开始生成最小生成树
	for (i = 1; i < G->Vertex_num; i++) {
		//这里表示有n个顶点要弄出n-1条边(包括v0有n个顶点)
		min = INFINITY;//初始化最小值为无穷
		j = 1;
		k = 0;//k用于存放当前权值最小边的其中一个下标
		while (j < G->Vertex_num) {
			//循环出了第一个外的全部顶点
			if (lowcost[j] != 0 && lowcost[j] < min) {
				//寻找没有完成(不为0),并且是最小的顶点的下标
				//也就是在可接触的最小权值边(lowcost)中选一个最小的
				min = lowcost[j];
				k = j;
			}
			j++;
		}
		//此时,k存放的就是当前可以接触到的最小权值边的一个顶点
		//adjvex[k]记录的是当前所能接触到的最小权值边的另一个顶点
		printf("(%d,%d)\n", adjvex[k], k);
		lowcost[k] = 0;//表示这个顶点已经加入到最小生成树中
		//接下来要寻找新的可以接触到的最小权值边
		for (j = 1; j < G->Vertex_num; j++) {
			if (lowcost[j] != 0 && G->arc[k][j] < lowcost[j]) {
				//k表示新的最小生成树顶点,可以通过它来寻找最小权值边
				lowcost[j] = G->arc[k][j];
				adjvex[j] = k;//找到了之后要把其中一个顶点改为k,表示到k的最小权值边
			}
		}
	}
}

Kruskal算法描述

首先将所有顶点都加入最小生成树的顶点集S中,然后每次选择权值最小的边,选择完之后要判断是否形成回路,如果形成回路的话就要重新选一条权值次之的边,如此循环,知道选择完n-1条边就构成了一棵最小生成树

一个鲜活的例子

代码实现(比较复杂)


//Kruskal算法的最小生成树//
//定义边集数组
typedef struct {
	int begin;//开始顶点的下标
	int end;  //结束顶点的下标
	int weight;//边的权重
}Edge;
//生成边集数组
void Create_array(MGraph *G,Edge edges[]) {
	int i,j,count=0;
	for (i = 0; i < G->Vertex_num; i++) {
		for (j = i + 1; j < G->Vertex_num; j++) {
			//因为是无向图,所以只要遍历矩形的一半(对角线也排除了)
			if (G->arc[i][j] != 0 && G->arc[i][j] != INFINITY) {
				//如果边存在的话
				edges[count].begin = i;//开始顶点的下标
				edges[count].end = j;//结束顶点的下标
				edges[count].weight = G->arc[i][j];//该边的权重
				count++;
			}
		}
	}
	//接下来给边集数组排序(只会冒泡法了)
	//出循环时边集数组的数量是count-1
	//排序要求从小到大
	for (i = 0; i < count; i++) {
		for (j = i + 1; j < count; j++) {
			if (edges[i].weight > edges[j].weight) {
				//如果前一个小于后一个的话就交换位置
				Edge temp;
				temp = edges[i];
				edges[i] = edges[j];
				edges[j] = temp;
			}
		}
	}
	//打印边集数组
	printf("边集数组为:\n");
	printf("\tbegin\tend\tweight\n");
	for (i = 0; i < count; i++) {
		printf("edges[%d] %d\t%d\t%d\n", i, edges[i].begin, edges[i].end, edges[i].weight);
	}
}
//查询函数,用于查找如果加入这条边的话是否存在存在回路
int Find(int *parent, int f) {
	while (parent[f] > 0) {
		//大于零就表示该边已经加入了最小生成树
		//要通过该边结点来寻找其他的可以加入最小生成树的边
		f = parent[f];
	}
	return f;
}
//算法主体
void MiniSpanTree_Kruskal(MGraph *G) {
	int i, n, m;
	Edge edges[MAXEDGE];//用于存放边集数组
	int parent[MAXVEX];	//用于存放最小生成树的边,0表示还没有加入最小生成树
						//数组下标表示边的起始点下标,数组内的值表示边的结束点下标
	//先生成边集数组
	Create_array(G, edges);
	//初始化parent数组
	for (i = 0; i < G->Vertex_num; i++) {
		parent[i] = 0;
	}
	//接着循环遍历每一条边
	printf("生成最小生成树(Kruskal算法):\n");
	for (i = 0; i < G->Edge_num; i++) {
		n = Find(parent, edges[i].begin);
		m = Find(parent, edges[i].end);
		if (n != m) {
			//不相等就表示不存在回路
			parent[n] = m;//表示加入最小生成树
			printf("(%d,%d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);
		}
	}
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值