并查集和等价类
上面的等价对,是因为一些共同的特征特点,成为等价关系(需要具体的环境)
必须有一个场景,才有等价关系
(聚合类算法,找最优)
例如,一个女生,有10个相亲对象,女生对这10个男生标号,1,2,3,…,10,0号男生有房有车,1号男生有房有车,所以0和1就等价,以此类推下去。第一次等价划分后,还可以继续细分,以身高体重颜值进行第二次等价划分,以此类推下去。
比如,我们有12个元素,初始化为-1,-1等负数代表是根节点。接着进行b处理,0和4等价,我们在数组的4下标位置存上0值,4的双亲在0位置,0的双亲是-2,就是树根,划分为一个等价关系。接着看,3和1等价,我们把数组下标3的位置的值置为-2,把数组下标1的位置存放3值,就是表示1的双亲在3下标,3下标的值为-2,就是根节点。以此类推下去,负数就是根节点,负数的绝对值就是代表有几个结点。
划分如下:
并查集的代码如下
#include<iostream>
using namespace std;
class UFSets
{
private:
int* parent;
int size;
public:
UFSets(int sz = 12) :size(sz)//构造函数,数组值均初始为-1
{
parent = new int[size];
for (int i = 0; i < size; ++i)
{
parent[i] = -1;
}
}
~UFSets()//析构函数
{
delete[]parent;
parent = nullptr;
size = 0;
}
void Print(int x)
{
printf("%3d", x);
for (int i = 0; i < size; ++i)
{
if (parent[i] == x)
{
Print(i);
}
}
}
public:
int Parent(int x)//寻找双亲的下标
{
while (parent[x] >= 0)
{
x = parent[x];
}
return x;
}
bool Union(int a, int b)//合并
{
bool res = false;
int pa = Parent(a);
int pb = Parent(b);
if (pa != pb)
{
parent[pa] += parent[pb];
parent[pb] = pa;
res = true;
}
return res;
}
void Print()//打印并查集,打印分组后的数据
{
int num = 1;
for (int i = 0; i < size; ++i)
{
if (parent[i] < 0)
{
printf("s%d ", num++);
Print(i);
printf("\n");
}
}
}
};
int main()
{
UFSets sets(12);
sets.Union(0, 4);
sets.Union(3, 1);
sets.Union(6, 10);
sets.Union(8, 9);
sets.Union(7, 4);
sets.Union(6, 8);
sets.Union(3, 5);
sets.Union(2, 11);
sets.Union(11, 0);
sets.Print();
return 0;
}
运行程序
并查集的问题及改进
并查集的加权规则
上图所示导致查询的速度慢,要尽可能让树扁一些。
改进操作
bool Union(int a, int b)//合并
{
bool res = false;
int pa = Parent(a);
int pb = Parent(b);
if (pa != pb)
{
if (parent[pa] > parent[pb])
{
parent[pb] += parent[pa];
parent[pa] = pb;
}
else
{
parent[pa] += parent[pb];
parent[pb] = pa;
}
res = true;
}
return res;
}
并查集的折叠规则
并查集解决图的问题
克鲁斯卡尔算法
我们解析一下
在这个图,我们先把边撤销掉,剩下一个一个的顶点,我们有一个优先级队列,存放着
克鲁斯卡尔算法就是把这些放在堆里面。
我们有0-5,5个下标
均初始化为-1
0-2是第一小的,我们从堆里面取出来,0和2下标是-1,我们把0下标置为-2,2下标置为0,0和2连为一个边。
第二小是3-5,3和5下标是-1,3下标的值置为-2,5下标的值置为3。
第三小是1-4,等于3,1下标和4下标是-1,我们把1下标置为-2,4下标置为3。
第四小是2-5,2下标值是0,0下标的值为-2,5下标值是3,3下标的值为-2,不在一个集合中,把0下标的值置为-4,3下标的值置为0。
接下来如果选0-3,就是环了,要舍弃。如果要2-3,是在同一个集合中,也是带环,舍弃。如果选1-2,1是-2,2是0,0下标值等于-4,我们就把0下标值改为-6,把1下标的值置为0。
如何判断图是不是来连通的?
如果只有1个集合,就是连通的,如果多于1个集合,图就不是连通的!