概念描述: 将编号分别为1.......N的N个对象划分为不相交集合, 在每个集合中,选择其中某一个元素代表所在集合。
常见的两种操作:
1. 合并两个集合
2. 查找某元素属于哪个集合
实现方法(1):
用编号最小的元素标记所在集合;
定义一个数组Set[ 1....N ], 其中Set[i]表示元素i所在的集合, 用下标1...N表示N个元素
构建数组:
当想知道下标3属于哪个集合, 返回 Set[3]=1 说明下标3这个元素属于1集合。
合并两个集合, a, b分别为两个集合标签, 如{1,3,7}集合的标签就是1, {2,5,9,10}集合的标签是2
需要遍历Set数组中的所有元素,更新需要修改的值(标签)
Mergel(a, b)
{
i = min(a, b)
j = max(a, b)
for(k=1; k<=N; k++)
{
if(Set[k] == j)
{
Set[k] = i;
}
}
}
查找x所属的集合
findl(Set[],x)
{
return Set[x];
}
合并操作会消耗大量资源,因为必须搜索全部元素, 可以改进算法,线性结构更改成树结构。
实现方法(2) Set[i]数组中保存的值是 索引 i 对应的父节点, 如4的父节点为2, 因为Set[4]=2。
每个集合用一棵" 有根数 "表示, 定义数组 Set[ 1........n]
Set[i] = i 则 i 表示本集合, 并不是集合对应的根 (如Set[2] = 2 , 说明2是集合对应的根)
Set[i] = j , 若 j 不等于 i, 则 j 是 i 的父节点(如节点7, Set[7] = 4, 说明4是7的父节点, Set[4]=2, 说明2是4的父节点, Set[2]=2, 说明是根节点)
当每个人都单独为一个集合,最坏情况需要查N次, 复杂度为O(N)
树形结构的查找和合并操作:
find2(Set[], x)
{
r = x;
while(Set[r] != r)
{
r = Set[r];
}
return r;
}
两集合的合并:(也就是将两个子树合并成一个树,a,b分别为两个树的根)
merge2(a, b)
{
Set[a] = b;
}
为了避免O(N)这种情况, 应该将深度小的树合并到深度大的树(也就是深度大的树做根节点)
应该判断树的高度再合并。
merge3(a, b)
{
if(height(a) == height(b))
{
height(a) = height(a) + 1;
Set[b] = a;
}
if(height(a) < height(b))
{
Set[a] = b;
}
else
{
Set[b] = a;
}
}
当有多组数据输入时候:
while(scanf("%d", &n), n)
{
scanf("%d", &m);
for(; m>0; m--)
scanf("%d %d", &x, &y)
}
n表示n个城市, m表示m个街道, 接下来m行输入x->y有一条街道,
当所有街道输入完毕,就会退出内层循环,重新输入n, 当输入n=0时候,退出外层循环
问题描述:
现在有n个城市, m个街道, 城市x和y之间街道,判断为了所有城市都连通,至少还需要新建几条街道。
输入数据:
5,3 ( 5表示有5个城市, 3表示一共有三条街道)
1,2 (表示x=1的城市 和 y=2的城市有一条街道)
2,4
2,5
解题思路:
首先初始化一个数组, 数组中每个元素都是一个单独集合。
开始输入x和y城市, 每次输入x和y城市,都判断x的根节点, y的根节点,如果是同一个节点,说明x和y在同一个集合,不用合并集合, 最终,把有通道连接的城市划分为一个集合, 集合中,每个城市之间是连通的, 会有多个合并成多个集合, 问题是为了连通者多个集合,还要建设多少条街道。
现在已经合并成多个集合,最后遍历数组,找有几个根节点,当 Set[i] == i, count++。当查到一个根节点, 那就计数器加1, 最终输出,T个集合会有T-1条通道
#include<stdio.h>
int bin[1002];
int find(int x)
{
int r = x;
while(bin[r] != r)
{
r = bin[r];
}
return r;
}
void merge(int x, int y)
{
int fx,fy;
fx = find(x);
fy = find(y);
if(fx != fy)
{
bin[fx] = fy;
}
}
int main()
{
int n,m,i,x,y,count;
while(scanf("%d",&n),n)
{
for(i=1; i<=n; i++)
{
bin[i] = i;
}
for(scanf("%d", &m); m>0; m--)
{
scanf("%d %d", &x, &y);
merge(x, y);
}
for(count=-1,i=1; i<=n; i++)
{
if(bin[i] == i )
{
count++;
}
}
printf("%d\n",count);
}
return 0;
}