#define SIZE 100
int UFSets[SIZE]; //集合元素数组(双亲指针数组)
void Initial(int S[]) //初始化
{
for (int i = 0; i < SIZE; i++)
S[i] = -1; //每个自成单元素集合
}
int Find(int S[], int x) //在并查集S中查找并返回包含元素x的树的根
{
while (S[x] >= 0) x = S[x]; //循环寻找x的根
return x; //根的S[ ]<0
}
void Union(int S[], int x, int y) //求两个不相交子集合的并集
{
int Root1 = Find(S, x);
int Root2 = Find(S, y);
if (Root1 == Root2) return; //要求Root1和Root2是不同的,且表示子集合的名字
S[Root2] = Root1; //将根Root2连接到另一根Root1下面
}
void Union1(int S[], int x, int y) //并集优化
{
int Root1 = Find(S, x);
int Root2 = Find(S, y);
if (Root1 == Root2) return; //要求Root1和Root2是不同的,且表示子集合的名字
if (S[Root2] > S[Root1]) //Root2结点数更少
{
S[Root1] += S[Root2]; //累加结点总数
S[Root2] = Root1; //小树合并到大树
}
else
{
S[Root2] += S[Root1]; //累加结点总数
S[Root1] = Root2; //小树合并到大树
}
}
int Find1(int S[], int x) //路径压缩
{
int root = x;
while (S[root] >= 0) root = S[root]; //循环找到根
while (x != root) //压缩路径
{
int t = S[x]; //t指向x的父结点
S[x] = root; //x直接挂到根结点下
x = t;
}
return root; //返回根结点编号
}
判断图(有向图/无向图)的连通分量数:初始时,将每个顶点看成是单独的集合,遍历邻接矩阵(或邻接表),当发现两个顶点之间存在路径时,将这两个顶点合并到一个集合中。当遍历结束后,统计并查集数组中双亲指针域里有几个负数,该值就是图的连通分量数。
判断图(只能是无向图)是否有环:在判断图的连通分量数的功能上,增加一个判断:如果遍历到两个顶点时,发现在此前的遍历中已经将这两个顶点合并到了一个集合,则说明此时图中必然存在环。
利用并查集判断图是否有环以及图中连通分量的个数
#include
#include
#define SIZE 50 //定义数组最大容量
typedef char ElemType; //结点数据类型定义为字符型
typedef struct
{
ElemType data; //数据域
int parent; //双亲结点域
}UFSets[SIZE]; //并查集类型
void Initial(UFSets S[]) //初始化
{
for (int i = 0; i < SIZE; i++)
{
S[i]->data = -999; //初始时不设结点值
S[i]->parent = -1; //每个自成单个元素集合,初始时父结点全部设为-1
}
}
void Create(UFSets S[], int i) //创建数组表信息
{
ElemType data; //结点数据域为字符型
int parent; //结点双亲域为整型
printf("\n输入结点的数据:");
getchar(); //获取数据域信息
scanf("%c", &data);
S[i]->data = data; //存数据信息
printf("——输入该元素的双亲结点对应的数组下标:");
scanf("%d", &parent);
S[i]->parent = parent; //双亲结点信息
}
void AdjustArrTable(UFSets S[], int num)
//调整数组双亲结点信息,用根结点的绝对值代表集合中元素的个数
{
for (int i = 0; i < num; i++)
{
int j = i;
while (S[j]->parent >= 0)
{
j = S[j]->parent; //不断向上找到根结点
if (S[j]->parent < 0) S[j]->parent -= 1;
//当该集合中每增加一个元素时,根结点的双亲信息值-1,取绝对值,表示当前集合中元素的个数
}
}
}
/*int Find(UFSets S[], ElemType x, int num) //在并查集S中查找并返回包含元素x的树的根的数组下标
{
int i = 0;
while (S[i]->data != x ) i++; //这里传入的是数据结点信息,不是数组下标,因此需要遍历一次
if (i >= num) return -999; //不存在值为x的数据结点
while (S[i]->parent >= 0) i = S[i]->parent; //通过双亲结点不断向上找树的根
return i; //返回根结点信息
}*/
/*bool Union(UFSets S[], char x, char y, int num) //求两个不相交子集合的并集
{
int Root1 = Find(S, x, num); //查找子集x的根结点
int Root2 = Find(S, y, num);; //查找子集y的根结点
if (Root1 == Root2) return false; //根结点相同则不进行合并
else
{
S[Root1]->parent += S[Root2]->parent; //集合中总结点数增加
S[Root2]->parent = Root1;
return true;
}
}*/
int Find1(UFSets S[], ElemType x, int num) //Find优化(即路径压缩)
{
int i = 0, j;
while (S[i]->data != x) i++; //这里传入的是数据结点信息,不是数组下标,因此需要遍历一次
if (i >= num) return -999; //不存在值为x的数据结点
j = i;
while (S[j]->parent >= 0) j = S[j]->parent; //通过双亲结点不断向上找树的根
while (S[i]->parent >= 0)
{
int t = S[i]->parent;
S[i]->parent = j;
i = t;
}
return j; //返回根结点信息
}
bool Union1(UFSets S[], ElemType x, ElemType y, int num) //Union优化(即将小集合并入大集合中)
{
int Root1 = Find1(S, x, num); //查找子集x的根结点
int Root2 = Find1(S, y, num);; //查找子集y的根结点
if (Root1 == Root2) return false; //根结点相同则不进行合并
if (abs(S[Root1]->parent) > abs(S[Root2]->parent))
{
S[Root1]->parent += S[Root2]->parent; //集合中总结点数增加
S[Root2]->parent = Root1;
}
else
{
S[Root2]->parent += S[Root1]->parent; //集合中总结点数增加
S[Root1]->parent = Root2;
}
return true;
}
void Print(UFSets S[], int num) //打印数组中结点的信息
{
for (int i = 0; i < num; i++)
printf("\n%c(%d)", S[i]->data, S[i]->parent); //打印数据信息以及双亲结点信息
}
int main()
{
UFSets S[SIZE];
int num;
Initial(S);
printf("-----创建集合-----");
printf("\n输入要创建的单个元素个数:");
scanf("%d", &num);
for (int i = 0; i < num; i++) Create(S, i);
AdjustArrTable(S, num);
printf("\n-----打印信息表-----");
Print(S, num);
ElemType data;
printf("\n\n-----在并查集S中查找并返回包含元素x的树的根-----");
printf("\n输入要查找的元素的数据:");
data = getchar();
scanf("%c", &data);
int i = Find1(S, data, num);
printf("包含元素%c的树的根的数据为:%c", data, S[i]->data);
printf("\n-----打印信息表-----");
Print(S, num);
ElemType x, y;
bool result;
printf("\n\n-----求两个不相交子集的并集-----");
printf("\n输入两个不相交的数据元素");
printf("\n输入第一个数据:");
x = getchar();
scanf("%c", &x);
printf("输入第二个数据:");
y = getchar();
scanf("%c", &y);
result = Union1(S, x, y, num);
printf("\n合并结点%c和%c所在的两个集合——合并成功了吗?——%d(0代表失败,1代表成功)", x, y, result);
if (result) Print(S, num);
return 0;
}
两种优化方式的运行结果:
scanf("\n%c",&c);
scanf(" %c",&c); //%c前面加空格,过滤回车
3.在接收字符前,使用getchar()来读取一次回车符号
getchar(); //专门用来读取上次输入的回车符号
scanf("%c",&c);
!此次代码读取字符采取的是这种形式
fflush(stdin); //清空输入流缓冲区的字符,注意必须引入#include头文件
scanf("%c",&c);