[基础算法 - 并查集]详细的并查集介绍以及各种操作讲解

61 篇文章 0 订阅

首先理解并查集的含义

并:可以合并

查:可以查询

集:集合组成

为什么我们需要并查集

在解决“连通块”,“城市修路”,“江湖门派”等问题时,使用并查集可以高效地解决问题。非常,非常高效。

并查集长啥样(同权并查集)

并查集是一种单边有向图,边代表的含义仅仅是属于关系,一般可以用数组实现。举个例子,我们现在有5个点(1 ~ 5),分别随机摆放。

我们初始化一个pre[]数组,指向某点的前导点(父节点),父节点默认为自身。

void init()
{
    for(int i = 1; i <= n; ++i)pre[i] = i;
}

 此时的每个点都指向自身,即pre[1] = 1,pre[2] = 2,pre[3] = 3,pre[4] = 4,pre[5] = 5;

如何合并

在合并之前,我们需要写一个函数来“找到根节点”,注意是根节点而不是父节点,根节点是父的父的父的父的父......最终的父亲才是根。由于单个点过于随机和不好处理,之后的合并,查询等操作都会在“根节点”上执行,无一例外。

int root(int x)//找到点 x 的根节点 
{
    while(pre[x] != x)x = pre[x];
    //只要x不是根节点,就一直回溯
    return x;
}

是不是非常简单?(这不是最终版的查找根节点的函数,后续还有路径压缩优化)

比如我们现在有如下的图(先别管怎么连起来的):

很明显pre[2] = 3,pre[3] = 4,pre[4] = 4。

那么 root( 2 ) 会返回4,root( 3 ) 也会返回4,这是显而易见的。

接下来,我们试着把 节点5 合并到 2 - 3 - 4 中,前面说过所有操作都在根上完成,那么我们就这样写合并函数(为了防止函数名冲突,特意没用merge这个名字)。

void mege(int u,int v)//将u v 节点合并
{
    pre[root(u)] = root(v);//u的根节点指向v的根节点
    //pre[root(v)] = root(u);反之也行
}

这样操作之后,图会有两种可能。下面参数中的的2 3 4可以随意替换,但是5必须有。

如果我们执行 mege(5,2)

 若执行mege(3,5)

霸特!无伤大雅,我们只要他们几个连起来就行,根是谁不重要,根相同才重要

如何查询

 比如我们想知道 2 与 4 是否连通,那么只需要判断是否同根

bool iscon(int u,int v)
{
    return root(u) == root(v);
}

甚至连注释都不想写了,这个道理也是显而易见的。

假设此时图为第二种。

我们想知道 1 和 3 是否连通,那么函数会返回:1 == 5 显然是 false

如果要查 2 和 3是否连通,那么函数返回:5 == 5 显然是 true

并查集的核心优化 - 路径压缩

如果图比较小,或者路径比较短,合并与查询速度都还行,但是如果有一条长度为10000甚至更长的路径,如下图:

 如果此时我们查询 1 与 2是否连通,或者 合并 1 和 5 这类操作,时间复杂度会达到惊人的O(n),而且是每一次操作都这么慢。

有没有办法优化呢?有,而且很简单,只需要稍微修改一下root函数就行了,你怎么不早说啊喂!

int root(int x)//找到点 x 的根节点
{
    int rx = x;//用rx代替x进行查找
    while(pre[rx] != rx)rx = pre[rx];
    //只要x不是根节点,就一直回溯
    pre[x] = rx;//直接将x指向根节点
    return rx;
}

这样的话,当我们进行第一次 查询 1 2连通 时,在执行root函数时1会直接指向根而放弃指向2(终究是错付了)

如下图

由于我们的操作都在根上完成,所以这样并不会影响操作,反而可以将速度提高很多。 

并查集的其他基本操作

查询连通块的个数,即根的个数

int rootsum()
{
    int cnt = 0;
    for(int i = 1; i <= n; ++i)if(pre[i] == i)cnt++;
    return cnt;
}

 讲完了,没了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值