prim算法 和 kruskal算法最小生成树C语言源代码

prim算法和kruskal视频

代码实现都是基于这个b站中视频的讲解

prim算法

1.初始化每一个结点和图
2.从一个结点开始search
(1)update 将以此结点开始的边加入,如果更小进行替换,父节点也要替换。
(2)scan 扫描找到最小的边, 已经选择了的点不考虑,这个新的结点进行递归。
(3)add 将这个结点变为已选择过。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int **graphics;
int n;
int sign;

typedef struct node {
	int number;   //编号
	bool select;   //是否已经被选择过
	int mindist;  //最短的路径权值
	int parent;   //父亲结点
}node; 

node *nodes;
//所有的初始化操作
void init() {
	sign = 0;
	printf("请输入最大结点数值");
	scanf("%d", &n);
	n = n + 1;
	graphics = (int**)malloc(sizeof(int*) * n);
	for (int i = 0; i < n; ++i) {
		graphics[i] = (int*)malloc(sizeof(int) * n);
		memset(graphics[i], 0, sizeof(int) * n);
	}
	nodes = (node*)malloc(sizeof(node) * n);
	for (int i = 0; i < n; ++i) {
		nodes[i].number = i;
		nodes[i].select = false;
		nodes[i].parent = -1;
		nodes[i].mindist = INT_MAX;
	}
	printf("请输入边的数量");
	int edgenumber;
	scanf("%d", &edgenumber);
	for (int i = 0; i < edgenumber; ++i) {
		printf("请输入第%d条边的第一个结点的序号", i + 1);
		int x;
		scanf("%d", &x);
		printf("请输入第%d条边的第二个结点的序号", i + 1);
		int y;
		scanf("%d", &y);
		int val;
		printf("请输入第%d条边的权值", i + 1);
		scanf("%d", &val);
		graphics[x][y] = val;
		graphics[y][x] = val;
	}
}

void search(int start) {
	if (sign >= n - 1) return;
	nodes[start].select = true;
	//update
	for (int i = 0; i < n; ++i) {
		if (graphics[start][i] != 0) {
			if (nodes[i].select == false) {//只能考虑没有选择过的结点
				if (nodes[i].mindist > graphics[start][i]) {
					nodes[i].mindist = graphics[start][i];
					nodes[i].parent = start;
				}
			}
		}
	}
	//scan
	int minsign = INT_MAX;
	int minnode;
	for (int i = 0; i < n; ++i) {
		if (nodes[i].select == false) {
			if (nodes[i].mindist < minsign) {
				minsign = nodes[i].mindist;
				minnode = i;
			}
		}
	}
	//add
	printf("连接%d-%d\n", nodes[minnode].parent, minnode);
	nodes[minnode].select = true;
	++sign;
	search(minnode); 
}

int main() {
	init(); 
	search(0);
	return 0;
}

在这里插入图片描述

在这里插入图片描述
prim算法的时间复杂度为O(n^2)(来自于search中递归n此和for循环n次)

kruskal算法

1.对边的权值从小到大排序排序
2.加入集合判断是否构成环,如果构成环则加入这条边否在跳过这条边
3.当选择边的数目等于结点数-1则结束算法输出选择的边。

#include<stdio.h>
#include<stdlib.h>
//建立边结构 
typedef struct edge {
	int v1;
	int v2;
	int val;
	bool select;
} edge;

edge *edges; //存储边信息 
int *vertexs;//存储结点父节点 
int vertex_number;//结点数量 
int edge_number;//边数量 
//初始化结点信息 
void initvertex() {
	printf("请输入结点数");
	scanf("%d", &vertex_number);
	vertexs = (int*)malloc(sizeof(int) * vertex_number);
	for (int i = 0; i < vertex_number; ++i) {
		vertexs[i] = i;
	}
}
//初始化边的信息 
void initedge() {
	printf("请输入边的数");
	scanf("%d", &edge_number);
	edges = (edge*)malloc(sizeof(edge) * edge_number);
	for (int i = 0; i < edge_number; ++i) {
		printf("请输入第一个点的值");
		scanf("%d", &edges[i].v1);
		printf("请输入第二个点的值");
		scanf("%d", &edges[i].v2);
		printf("请输入第%d条边的值", i + 1);
		scanf("%d", &edges[i].val);
		edges[i].select = false; 
	}
}
//寻找祖先结点并压缩路径 
int find(int x) {
	int r = vertexs[x];
	while (r != vertexs[r]) {
		r = vertexs[r];
	}
	int i = x, j;
	while (i != r) {
		j = vertexs[i];
		vertexs[i] = r;
		i = j;
	}
	return r;
}
//合并两个结点 
void merge(int x, int y) {
	int xp = find(x);
	int yp = find(y);
	vertexs[xp] = yp;
}

int comp(const void *_a, const void *_b) {
	edge a = *(edge*)_a;
	edge b = *(edge*)_b;
	return a.val - b.val;
}
//先排序然后从最小边开始判断是否有环如果没环则加入集合并进行下一条边的判断 
void kruskal() {
	initvertex();
	initedge();
	qsort(edges, edge_number, sizeof(edge), comp);
	int sign = 0;
	for (int i = 0; i < edge_number && sign != vertex_number - 1; ++i) {
		if (find(edges[i].v1) != find(edges[i].v2)) {
			merge(edges[i].v1, edges[i].v2);
			edges[i].select = true;
			++sign;
		}
	}
}
//输出选择的边 
void show() {
	int minsum;
	for (int i = 0; i < edge_number; ++i) {
		if (edges[i].select) {
			minsum += edges[i].val;
			printf("选择的边为%d-%d\n", edges[i].v1, edges[i].v2);
		}
	}
	printf("最小的权值和为%d", minsum);
}

int main() {
	kruskal();
	show();
	return 0;
}

在这里插入图片描述

在这里插入图片描述
kruscal的时间复杂度为O(n*logn)(取决于排序的时间复杂度)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值