Kruskal算法

来源:互联网 发布:大话设计模式基于java 编辑:程序博客网 时间:2024/05/09 22:03

下面例子使用的图。
/* *克鲁斯卡尔算法 *步骤 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 21int 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;}}//并查集的findint 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 102 1 63 1 13 2 54 1 55 2 35 3 66 3 46 4 26 5 64 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)。 */


原创粉丝点击