Kruskal算法


下面例子使用的图。

/*
 *克鲁斯卡尔算法
 *步骤
 1:给所有的边按照权值从小到大排列。
 2:情况1:u和v在同一个连通分量中,那么加入(u,v)之后就会形成环了,因此不能选择。
 	情况2:u和v不同一个连通分量中,那个加入一定是最优的。
	原因是:用反证法
	如果不加这条边能得到一个最优解T,则T + (u,v)一定有且只有一个环,而且环中至少有一条边
	(u',v')的权值大于或者等于(u,v)的权值,删除该边后,得到的新树T'不会比T更差,因此加入(u,v)
	不会比不加入差
 */
#include "stdio.h"
#include "stdlib.h"
#define MAX 21

int v[MAX];
int u[MAX];
int w[MAX];
int set[MAX];
int r[MAX];
int compare (int i,int j) {
	return w[i] - w[j];
}


//插入排序
void sort (int num[],int n,int (*compare)()) {
	int i,j,key;
	for (i = 1;i < n;i++) {
		key = num[i];
		j = i - 1;
		while (j >= 0 && compare(num[j],key) >= 0) {
			num[j+1] = num[j];
			j--;
		}
		num[j+1] = key;
	}	
}

//并查集的find
int find (int i) {
	if (set[i] == i) {
		return i;
	}else {
		return find (set[i]);
	}
}

//Kruskal算法
//n:顶点的个数,m:边的个数
//v[i],u[i],w[i]分别表示第i条边的关联的顶点的序号和权值。
//排序后的结果存放在r[i]中。这个并没有对v[i],u[i],w[i]对象进行排序,而是对r[i]排序,排序后的第i小的边存放在r[i]中,这就是间接排序-
//排序的关键字是对象的”代号“,而不是对象本身。
int kruskal (int n,int m) {
	int result = 0;
	int i;
	//初始化并查集
	for (i = 0;i < n;i++) {
		set[i] = i;
	}
	//初始化边序号
	for (i = 0;i < m;i++) {
		r[i] = i;
	}
	sort (r,m,compare);//对边间接排序
	for (i = 0;i < m;i++) {
		int tmp = r[i];
		int t1 = find (u[tmp]);
		int t2 = find (v[tmp]);
		if (t1 != t2) {
			result += w[tmp];
			set[t1] = t2;
		}
	}		
	return result;
}

/**
 *input:
6 10
2 1 6
3 1 1
3 2 5
4 1 5
5 2 3
5 3 6
6 3 4
6 4 2
6 5 6
4 3 5
 *result:
 15
 */

int main () {
	int n,m;
	int i,_v,_u,_w;
	int result;
	scanf ("%d%d",&n,&m);
	for (i = 0;i < m;i++) {
		scanf ("%d%d%d",&_u,&_v,&_w);
		u[i] = _u;
		v[i] = _v;
		w[i] = _w;
	}
	for (i = 0;i < m;i++) {
		printf ("%d-%d:%d\n",u[i],v[i],w[i]);
	}

	result = kruskal (n,m);

	printf ("%d\n",result);
	return 0;
}

/*
 Union-Find-Set(并查集)
 并查集是用树来表示集合。
eg:包含1,2,3,4,5,6图的3个连通分量{1,3},{2,5,6},{4}3颗树来表示,
这3棵树是什么形态是无关紧要的,只要一棵树包含1,3二个点,一棵包含
2,5,6这3个点,一棵包含4这个点,。并规定每棵树的根结点是这棵树所
对应的集合的代表元。

如果把x的父结点保存在p[x]中(如果x没有父亲,则用p[x]=x)。
 */


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值