首先是集合的基本数据类型,这里的查找我使用了规模对比的优化方式。
//用树实现集合的表现形式,注意是树不是二叉树,这是一种包含指向爸爸下标的数据结构
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 100
typedef struct
{
int data;
int parent;//负数表示为集合首个元素(即根节点),绝对值为数量
}SetType;
SetType S[10];//假设有10个元素
//查找某个元素所在的集合
int Find(SetType S[], int X)
{
int i;
for (i = 0; i < MaxSize && S[i].data != X; i++)
{
;
}
if (i > MaxSize) return -1;//未找到X,返回-1
for (; S[i].parent >= 0; i = S[i].parent);//不断地向上找爸爸,直到发现爸爸值为-1
return i;//找到X所属的集合,返回树根节点的数组S中的下标
}
//用for循环真的反人类,不如while舒服,这里用while写一下
int FindWhile(int item,SetType S[])
{
int i;
while (S[i].data != item) i++;//先找到i
while (S[i].parent >= 0) i = S[i].parent;//向上找爸爸,直到爸爸小于0
return i;//集合首元素
}
//集合的并运算
/*
1.找到元素所在的根节点
2.然后将一个树的parent指向另一个树即可
*/
void Union(SetType S[], int X1, int X2)
{
int Root1, Root2;
Root1 = FindWhile(X1, &S[MaxSize]);
//但是擅长指针的你,为什么不写成下面这种形式呢
Root2 = FindWhile(X2, S);
if ((Root1 != Root2) && (-Root1 > -Root2))//root1的数量比root2的数量多
{
Root1 = Root1 + Root2;//将头合并为两个负数的和
S[Root2].parent = Root1;
}
else
{
Root2 = Root1 + Root2;//将头合并为两个负数的和,注意我这里用的是按照规模归并
S[Root1].parent = Root2;
}
将一个集合的头指向另一个集合的父亲,那么现在roo1就是并集之后的集合首元素
}
/*
为了查找的方便,我们希望能够把小的,元素少的集合并到大的,元素多的集合。
那么,我们可以不把parent统一规定为-1.可以规定为数量的负数。
*/
下面是一道堆的例题,采用了一种新的数据结构形式。
/*
集合的简化表示方法
1.我们将数量可数的元素映射到0到N-1
2.然后建立数组A[N],下标表示的是元素的data,数组中存放的内容是parent的值
*/
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 1000
typedef int ElementType;
typedef int SetName;
typedef ElementType SetType[MaxSize];
//比如说 int S[100] 可以用 SetType S
SetName Find(SetType S, ElementType X)
{
while (S[X] > 0)//父节点大于0,代表不是集合首元素
{
X = S[X];//S[X]代表的是父节点,赋值给X那么再S[X],就相当于是父亲的父亲
}
return X;//直到找到S[X]<0的,那么就返回数据X,X的值就是集合首元素的值
}
//路径压缩下的Find
SetName FindRar(SetType S, ElementType X)
{
if (S[X] < 0) return X;
else
return S[X] = Find(S, S[X]);
//这句话做了三件事,1.先找到根2.把根变成X的父节点3.再返回根
}
//按秩归并
//一多一少,高度不变,同样高度的归并,高度++;
void Union(SetType S, SetName Root1, SetName Root2)
{
//显然Root1代表的是S下标,同时表示数据
//两个集合的并只需要把一个的S[X]指向另一个
if(-S[Root2] > -S[Root1])//如果树2比树1高,那么树二就是合并后的树
S[Root1] = Root2;
else
{
if (S[Root1] == S[Root2])
S[Root1]--;
S[Root2] = Root1;
}
//S[Root2]指的是Root2这个值的爸爸,原来是-1,后来变成了Root1
}
void Init(SetType S, ElementType num)
{
for (int i = 0; i < num; i++)
S[i] = -1;
}
/*
题目:File Transfer
5 五台计算机
C 3 2 3和2之间能联通吗?
I 3 2 将3和2连接在一起
C 1 5 1和5能联通吗?
I 4 5
I 2 4
C 3 5
S 退出
然后输出系统中有几个联通集
*/
void Input_connection(SetType S)
{
ElementType com1, com2;
scanf("%d %d", &com1, &com2);
int Root1 = Find(S, com1 - 1);
int Root2 = Find(S, com2 - 1);
if(Root1 != Root2)
Union(S, Root1, Root2);
printf("%d 和 %d 现在连接在了一起\n ",com1,com2);
}
void Check_connection(SetType S)
{
ElementType com1, com2;
scanf("%d %d", &com1, &com2);
if (Find(S, com1 - 1) == Find(S, com2 - 1))
printf("能够连通\n");
else
printf("不能够连通\n");
}
void Check_networks(SetType S,int com_num)
{
int num = 0;
for (int i = 0; i < com_num; i++)
{
if (S[i] < 0) num++;
}
printf("目前有 %d 个连通区域\n",num);
}
int main()
{
SetType S;
int computer_num = 0;
char in = 0;
printf("请告诉我一共有多少台电脑\n");
scanf("%d", &computer_num);
Init(S, computer_num);
do
{
scanf("%c", &in);
switch (in)
{
case 'I':Input_connection(S); break;
case 'C':Check_connection(S); break;
case 'S':Check_networks(S,computer_num); break;
default:break;
}
} while (in != 'S');//只要输入的字符不等于S,那么就这么循环
return 0;
}