算法总结(1)-并查集与带权并查集

并查集是我在进入acm后学的第一个数据结构了,因为思路简单和代码短我倒是很快就记住了,但是深入理解可能就差了一些,比如真实复杂度之类的。
那第一篇总结就写写并查集好了

并查集

并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。 -来自百度百科

引入

并查集一般用于解决元素的分组问题,很多时候可以用来合并多个元素到一个集合从而达到简化问题的作用。
并查集的思想就是,最初,每个元素都各自属于一个集合,即可以定义一个数组pre[i]代表i元素的祖先,因为最开始每个元素单独为一个集合,所以其祖先就是自己。
接下来若两个元素产生关系,需要将两个元素纳入同一个集合,那这时需要的就是将其中一个元素的祖先指向另一个元素,他们的最大(等级最高,也就是处于树根位置的祖先)祖先相同(祖先的祖先也是我的祖先),就可以认为他们处于同一个集合。
因此对于这个数据结构我们需要合并和查询两种操作,分别用来将两个集合合并为一个集合,和查询一个节点的最大祖先。

查询

查询操作就是查询到该元素的最大祖先,最大祖先有一个特征,就是他的祖先是指向自己的,因此,代码如下。

int Find(int x)
{
    return x == pre[x] ? x : Find(pre[x]);
}

代码非常简单,使用递归查找祖先指向自己的元素即可,该元素就为这个集合的最大祖先。

合并

合并操作,就是指将两集合合并到一起,最简单的方法,就是让两个集合的最大祖先产生联系,这样他们两个集合的最大祖先相同,即代表所有元素处于同一个集合。

void union(int x,int y)
{
    pre[Find(x)] = Find(y);
}
同样一行代码解决,直接将x的最大祖先的祖先指针指向y的最大祖先即可。这样x与y的最大祖先相同也就代表他们在同一个集合内了。

路径压缩

当我们需要合并的元素过多时,在最坏情况下,并查集的树形结构会退化为一条链,这样Find操作的复杂度就会退化到O(n)。
那如何尽量避免这种情况呢,就是下面提到的路径压缩。
当我们在查询一个元素的祖先节点时,可以在递归返回最大祖先时,顺便将路径上经过的元素的祖先指针全部指向最大祖先。同样在Find查询操作时就可以顺遍完成。
路径压缩查询代码

int Find(int x)
{
    return x == pre[x] ? x : Find(pre[x]);
}

按秩合并

按秩合并是指将退化的并查集中每个集合尽量优化为树状结构。也许有人认为不是已经路径压缩了,就不需要再次优化树结构了。大多数情况下是如此的。但是在极端情况下,即每次新增节点均与该集合的祖先节点相连,即运行合并操作union(x,y)时,xy均为祖先节点,这时union操作中的Find操作并没有进行递归查询,也就没有进行路径压缩。这时,并查集中的树的深度仍然很高,也就是操作仍然需要消耗大量时间。因此按秩合并在极端情况下仍是有必要的.

inline void merge(int i, int j)
{
    int x = Find(i), y = Find(j);
    if (rank[x] <= rank[y])
        pre[x] = y;
    else
        pre[y] = x;
    if (rank[x] == rank[y] && x != y)
        rank[y]++;
}

代码中rank即为秩,也就是节点在树中的深度。最初应初始化为1.

带权并查集

带权并查集的含义就是,将元素与其父亲节点的关系存储下来。
而在大多的题目中,关系一般是存在循环的,比如经典例题POJ1182
食物链中三种关系是循环的,设0为捕食1为同级2为被捕食的时候,我们可以发现,若a与b的关系为捕食,b与c的关系为同级,那么a与c的关系也是为捕食,则
那么查找操作可以改为

int Find(int x){ 
    if(x == pre[x]) return x;
    else{   
        int temp = pre[x];
        pre[x] = Find(pre[x]); 
        rela[x] = (rela[x] + rela[temp]) % 3; // 压缩路径关系
    }
    return pre[x];
}

同样,合并操作也可以改为

void union(int x, int y, int r){
    int p = Find(x);
    int q = Find(y);

    if(p != q){
        pre[p] = q; 
        rela[p] = (rel[y] - rel[x] + r + 3) % 3;
    }
}

和普通并查集的区别仅仅在于需要多考虑一下关系的更新即可。

学习总结 文章被收录于专栏

对算法和一些其他知识的总结吧,看看自己到底几斤几两~

全部评论

相关推荐

行云流水1971:这份实习简历的优化建议: 结构清晰化:拆分 “校园经历”“实习经历” 板块(当前内容混杂),按 “实习→校园→技能” 逻辑排版,求职意向明确为具体岗位(如 “市场 / 运营实习生”)。 经历具象化:现有描述偏流程,需补充 “动作 + 数据”,比如校园活动 “负责宣传” 可加 “运营公众号发布 5 篇推文,阅读量超 2000+,带动 300 + 人参与”;实习内容补充 “协助完成 XX 任务,效率提升 X%”。 岗位匹配度:锚定目标岗位能力,比如申请运营岗,突出 “内容编辑、活动执行” 相关动作;申请市场岗,强化 “资源对接、数据统计” 细节。 信息精简:删减冗余表述(如重复的 “负责”),用短句分点,比如 “策划校园招聘会:联系 10 + 企业,组织 200 + 学生参与,到场率达 85%”。 技能落地:将 “Office、PS” 绑定经历,比如 “用 Excel 整理活动数据,输出 3 份分析表;用 PS 设计 2 张活动海报”,避免技能单独罗列。 优化后需强化 “经历 - 能力 - 岗位需求” 的关联,让实习 / 校园经历的价值更直观。 若需要进一步优化服务,私信
实习,投递多份简历没人回...
点赞 评论 收藏
分享
老粉都知道小猪猪我很久没更新了,因为秋招非常非常不顺利,emo了三个月了,接下来说一下我的情况吧本人是双非本&nbsp;专业是完全不着计算机边的非科班,比较有优势的是有两段大厂实习,美团和字节。秋招面了50+场泡池子泡死的:滴滴&nbsp;快手&nbsp;去哪儿&nbsp;小鹏汽车&nbsp;不知名的一两个小厂其中字节13场&nbsp;两次3面挂&nbsp;两次2面挂&nbsp;一次一面挂其中有2场面试题没写出来,其他的都是全a,但该挂还是挂,第三次三面才面进去字节,秋招加暑期总共面了22次字节,在字节的面评可以出成书了快手面了8场,2次实习的,通过了但没去,一次2面挂&nbsp;最后一次到录用评估&nbsp;至今无消息滴滴三面完&nbsp;没几天挂了&nbsp;所有技术面找不出2个问题是我回答不上来的,三面还来说我去过字节,应该不会考虑滴滴吧,直接给我干傻了去哪儿一天速通&nbsp;至今无消息小鹏汽车hr&nbsp;至今无消息美团2面挂&nbsp;然后不捞我了,三个志愿全部结束,估计被卡学历了虾皮二面挂&nbsp;这个是我菜,面试官太牛逼了拼多多二面挂&nbsp;3道题也全写了&nbsp;也没问题是回答不出来的&nbsp;泡一周后挂腾讯面了5次&nbsp;一次2面挂&nbsp;三次一面挂,我宣布腾讯是世界上最难进的互联网公司然后还有一些零零散散的中小厂,但是数量比较少,约面大多数都是大厂。整体的战况非常惨烈,面试机会少,就算面过了也需要和各路神仙横向对比,很多次我都是那个被比下去的人,不过这也正常,毕竟谁会放着一个985的硕士不招,反而去招一个双非读化学的小子感觉现在互联网对学历的要求越来越高了,不仅仅要985还要硕士了,双非几乎没啥生存空间了,我感觉未来几年双非想要进大厂开发的难度应该直线上升了,唯一的打法还是从大二刷实习,然后苟个转正,不然要是去秋招大概率是炮灰。而且就我面字节这么多次,已经开始问很多ai的东西了,你一破本科生要是没实习没科研懂什么ai啊,纯纯白给了
不知名牛友_:爸爸
秋招你被哪家公司挂了?
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务