最近学习简单不相交集(Disjoint-set),理论比较复杂,但它的结构简单,容易实现。
Disjoint-set记录一组集合S,其中任意两个合并的集合都是不相交的。
该集合上的基本操作有三个:
1、MakeSet(i):建立一个新集合,其仅有的成员由i所指向。要求该元没有在其他集合中出现过。
2、FindSet(i):返回一个指向包含i集合代表的指针。
3
、
Union(i, j)
:将包含
i, j
的动态集合合并为一个新集合,并在两集合中选择一个代表作为新集合的代表。
写了个最小生成树的程序,熟悉下Disjoint-set的基本操作,热热手。
/*
*Copyright (c) 2007, cn.edu.seu.cose
*File: disjoint.h
*
*Version: 1th
*Author: knightzzy
*Date: 2007.5.4
*/
#ifndef _DISJOINT_H
#define _DISJOINT_H
#define INFINITE 500000
#define MAXN 10
int set[MAXN], rank[MAXN];
int FindSet (int i);
void MakeSet (int i);
void Link (int i, int j);
void Union (int i, int j);
#endif
/*
*Copyright (c) 2007, cn.edu.seu.cose
*File: MCSTMain.c
*Description: Main Function, Minimum-cost Spanning Tree
*
*Author: knightzzy
*Date: 2007.5.4
*/
#include <stdio.h>
#include <string.h>
#include "disjoint.h"
int main (void)
{
int n, k, i, j, map[MAXN][MAXN];
int v1, v2, edgeCost, sumCost=0;
memset (map, 0, sizeof(map));
printf ("请输入顶点数和边数:/n");
scanf ("%d %d", &n, &k);
printf ("输入顶点1、顶点2和边的权值/n");
while (k--)
{
scanf ("%d%d%d", &v1, &v2, &edgeCost);
map[v1][v2] = map[v2][v1] = edgeCost;
}
for (i=0; i<n; i++)
{
MakeSet (i);
}
for (k=1; k<n; k++)
{
edgeCost = INFINITE;
for (i=0; i<n; i++)
{
for (j=0; j<n; j++)
{
if (map[i][j] && (FindSet(i)!=FindSet(j)))
{
if (map[i][j] < edgeCost)
{
v1=i;
v2=j;
edgeCost=map[i][j];
}
}
}
}
printf ("(%d,%d) ", v1, v2);
sumCost += edgeCost;
Union (v1, v2);
}
printf ("/n该最小生成树的开销是:%d/n", sumCost);
return 0;
}
/*
*Copyright (c) 2007, cn.edu.seu.cose
*File: disjoint.c
*Description:
*
*Author: knightzzy
*Date: 2007.5.4
*/
#include "disjoint.h"
int FindSet (int i)
{
if (set[i] != i)
{
set[i] = FindSet (set[i]);
}
return set[i];
}
void MakeSet (int i)
{
set[i] = i;
rank[i] = 0;
}
void Link (int a, int b)
{
if (rank[a] > rank[b])
{
set[b] = a;
}
else
{
set[a] = b;
if (rank[a] == rank[b])
{
rank[b]++;
}
}
}
void Union (int i, int j)
{
Link (FindSet(i), FindSet(j));
}
------------------------------------------------------------
input:
4 5
0 1 1
0 2 5
0 3 2
1 2 3
2 3 1
output:
(0,1) (2,3) (0,3)
4