克鲁斯卡尔算法求最小生成树

1.克鲁斯卡尔算法

    克鲁斯卡尔算法的核心思想是从边集出发,逐步把代价最小且不与已经加到最小生成树的边集构成回路的边加入到最小生成树的边集中,直到求出构成最小生成树的n-1边(n是图的顶点数)。算法的基本步骤如下。

  1. 对边集进行排序。
  2. 从权值最小的边开始,如果这条边连接的两个节点于图G中不在同一个连通分量中,则添加这条边到图G中。(简单的说就是试探性的加入这条边看加入后是否构成环)。
  3. 重复2,直至图G中所有的节点都在同一个连通分量中。

    非常推荐你去看这个视频!!!

2.具体实现。

    以邻接矩阵的形式存储图的关系,测试图和具体代码如下。
在这里插入图片描述
测试数据:

9 15
0 1 10
0 5 11
1 2 18
1 8 12
1 6 16
2 8 8
2 3 22
3 8 21
3 6 24
3 7 16
3 4 20
4 7 7
4 5 26
5 6 17
6 7 19
#include <cstdio>
#define MAXVER 20
#define INFINITY 1000000
typedef struct {
	int adjacentMatrix[MAXVER][MAXVER];//图的邻接矩阵 
	int numVertexes, numEdges;//图的顶点的数量,边的数量 
} Graph, *pGraph;
typedef struct {
	int begin;//边的起点 
	int end;//边的终点 
	int weight;//权值 
} Edge, *pEdge;
//初始化图 
void initGraph(pGraph G) {
	scanf("%d%d", &G->numVertexes, &G->numEdges);
	//初始化邻接矩阵 
	for(int i = 0; i < G->numVertexes; i++) {
		for(int j = 0; j < G->numVertexes; j++) {
			if(i == j) {//到自身的代价为0 
				G->adjacentMatrix[i][j] = 0;
			} else {
				G->adjacentMatrix[i][j] = INFINITY;
			}
		}
	}
	//输入边的信息 
	for(int i = 0; i < G->numEdges; i++) {
		int rowIndex, colomnIndex;
		scanf("%d%d", &rowIndex, &colomnIndex);
		scanf("%d", &G->adjacentMatrix[rowIndex][colomnIndex]);	
	}
	//邻接矩阵关于对角线对称完善邻接矩阵 
	for(int i = 0; i < G->numVertexes; i++) {
		for(int j = i; j < G->numVertexes; j++) {
			G->adjacentMatrix[j][i] = G->adjacentMatrix[i][j];
		}
	}
}
//交换两条边的信息 
void swap(pEdge one, pEdge two) {
	int temp;
	temp = one->begin; one->begin = two->begin; two->begin = temp;
	temp = one->end; one->end = two->end; two->end = temp;
	temp = one->weight; one->weight = two->weight; two->weight = temp;
}
//冒泡排序 
void sort(pEdge edges, int len) {
	for(int i = 0; i < len; i++) {
		for(int j = i + 1; j < len; j++) {
			if(edges[i].weight > edges[j].weight) {
				swap(&edges[i], &edges[j]);
			}	
		}
	}
}
//查找连线顶点的尾部下标,可以沿着parent数组找连通分量
int find(int *parent, int f) {
	while(parent[f] > 0) {
		f = parent[f];
	}
	return f;
}
//克鲁斯卡尔算法求最小生成树 
void miniSpanTree(Graph G) {
	int parent[G.numVertexes];//用来存储已经加到最小生成树的点,用于判断新加的边是否构成回路 
	for(int i = 0; i < G.numVertexes; i++) {
		parent[i] = 0;
	}
	Edge edges[G.numEdges];
	int cnt = 0;
	//利用邻接矩阵初始化边集 
	for (int i = 0; i < G.numVertexes-1; i++)
	{
		for (int j = i + 1; j < G.numVertexes; j++)
		{
			if (G.adjacentMatrix[i][j] < INFINITY)
			{
				edges[cnt].begin = i;
				edges[cnt].end = j;
				edges[cnt].weight = G.adjacentMatrix[i][j];
				cnt++;
			}
		}
	}
	sort(edges, G.numEdges);//对边集进行排序 
	int sum = 0; 
	printf("最小生成树:\n");
	for(int i = 0; i < G.numEdges; i++) {
		int n = find(parent, edges[i].begin);
		int m = find(parent, edges[i].end);
		if(m != n) {//不构成环,该边就可以加入最小生成树中了 
			parent[n] = m;//将此边的结尾顶点放入下标为起点 
			printf("(%d, %d) %d\n", edges[i].begin, edges[i].end, edges[i].weight);
			sum += edges[i].weight;
		}
	} 
	printf("权值和:%d", sum);
}
int main(int argc, char const **argv) {
	Graph G;
	initGraph(&G);
	miniSpanTree(G);
} 
  • 4
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值