算法基础 - 并查集
来源:互联网 发布:孕妇不能吃薄荷糖 知乎 编辑:程序博客网 时间:2024/05/16 07:24
并查集
在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。
问题举例
例如:
给你一堆人的名字,叫:a , b, c, d, e, f, g
然后呢,告诉你们这些人是有亲戚关系的,每次只告诉你两个人之间是有亲戚关系的,然后给两个人,询问他们是不是有亲戚关系。
解答:
最简单的办法是,最开始每个人都是独立的,每个人都是一个集合,{a}, {b}, {c}, {d}, {e}
这样子,然后假如告诉你说a,b
两个人是有亲戚的关系,那么就把这两个人放入一个集合里。变成{a,b}
,这样他们在查询的时候就知道有亲戚关系了。
假如有{a,b}, {c,d}
两个集合,然后告诉你b,c
有亲戚关系,就要变成{a, b, c, d}
这样四个人就都有亲戚关系了。
那么如何表示呢? 其实假如这些人之间是有长辈关系的话,就比较容易明白了,例如:b是a的长辈,c是b的长辈,c是d的长辈:那么就记成:parent[a] = b, parent[b] = c, parent[d] = c
这里的parent
就是一个映射的数组。
搜索的时候就搜索他们的最上面的长辈就好了,因为a -> b -> c
那么a
的长辈就是c
。而d -> c
那么d
的长辈也是c,那么假如问a和d
的话,他们两个就是有亲戚了。
集合表示
这里只记录每个人的第一个长辈是谁,就是map[a] = b
这样子来记。假如是亲戚,并没有说谁是长辈,那么表示的时候,就需要注意了。例如:a b
是亲戚关系,那么记录map[a] = b
,假如a c
也是亲戚关系,再记录map[a] = c
就会出现覆盖的情况,这个时候要知道,这是一个链式结构,这个链式结构是有一个头的,这个头指向的是自己,所以每次只要用链表的头部就可以了。
代码表示
#include <iostream>#include <unordered_map>#include <string>#include <cstring>using namespace std;unordered_map<string, string> map;string find_parent(string name){ if(map.find(name) == map.end()){ return name; } if(name == map[name]){ return map[name]; } map[name] = find_parent(map[name]); //这里进行状态压缩了 return map[name];}int main(){ int T; cin>>T; while(T--){ int s; string name_1, name_2; cin>>s; cin>>name_1>>name_2; if(s == 0){ name_1 = find_parent(name_1); name_2 = find_parent(name_2); if(name_1 == name_2){ continue; } map[name_1] = name_2; }else{ if(find_parent(name_1) == find_parent(name_2)){ cout<<"yes"<<endl; }else{ cout<<"no"<<endl; } } } return 0;}
状态压缩
所谓的状态压缩就是,假如我们是a -> b -> c -> d
的话,那么我们找到a的最祖先的节点就是d,也就是需要寻找四次,如果两个都是要寻找四次,那么判断两个节点是不是在一个祖先的时候,就需要8次操作,然后我们进行状态压缩,也就是把上面的链式结构,变成多个子节点指向父节点的结构,a -> d, b -> d, c->d d->d
这样每次查找的时候就可以一次就查找到了。
假如不状态压缩的话:
string find_parent(string name){ if(map.find(name) == map.end()){ return name; } if(name == map[name]){ return map[name]; } return find_parent(map[name]);}
如果进行状态压缩的话就要把当前节点,指向自己的祖宗。
string find_parent(string name){ if(map.find(name) == map.end()){ return name; } if(name == map[name]){ return map[name]; } map[name] = find_parent(map[name]); //这里状态压缩 return map[name];}
以上。
- 算法基础 - 并查集
- 算法基础篇(9)------并查集
- 零基础学并查集算法
- 零基础学并查集算法
- 并查集基础
- 并查集基础
- 并查集基础
- 并查集基础
- 并查集基础
- 并查集基础
- 基础 并查集
- 并查集基础
- 并查集基础
- 并查集基础
- 并查集基础
- 查并集基础
- 并查集基础
- 并查集基础
- Java序列化和反序列化
- BZOJ 2149 拆迁队 斜率优化DP 主席树
- Eclipse 反编译插件安装jad(转)
- 上线步骤总结
- ensp之设备基础配置 CLI
- 算法基础 - 并查集
- hdu1850Being a Good Boy in Spring Festival
- 沉浸式接入与信息安全
- 2013年第四届蓝桥杯C/C++程序设计本科B组决赛 高僧斗法(编程大题)
- 使用cgroup限制内存使用
- format display on systemverilog
- 更新二进制位
- JAVA JUnit测试框架
- hdu1422 重温世界杯(DP)