并查集的基础及其路径压缩(亲戚)
2018-02-08 20:28
260 查看
并查集,是一种十分简单但有实用的数据结构,它其实是一种森林.
这种森林支持合并的操作,但却不支持删除.
它是一棵树,但它这棵树的空间却只有O(n).
每个点i都表示一个节点,还有father[i]表示它的父节点.
它是一种用于判断两个点是否有关系的数据结构.
它的具体实现是这样的:
1.并查集用数组father来储存各个点的祖先.
2.一般并查集只有两个函数:初始化与查找一个点的祖先.
3.初始化:每个点在最开始的祖先都是自己,一般不特别写函数.
4.查找一个点的祖先:用root(u)代表查找x的祖先,它的思想就是不断递归地寻找当前点u的father,直至u的father是自己.
5.在root的基础上,可以再写一个合并的函数,思想为将y的祖先的父亲定为x.
6.查找x与y是否在一个家族里,就比较x与y的祖先是否相同
于是,并查集大概可以这样实现:
每个人都有很多亲戚,而有时亲戚太多就不好认,于是就有人来提出一个问题,给出一些亲戚关系,之后让你判断x与y是否是亲戚.
这个问题可以用并查集完成,就将给出亲戚关系当做合并,判断亲戚关系当成判断是否在同一家族就行了.
每次合并最差时间复杂度O(n),每次查找时间复杂度O(n),慢得如同跑dfs回溯.
但其实我们可以进行优化,因为我们可以再递归时顺便把每个点的父节点直接改为祖先节点,这样做可行的理由是并查集不支持删除.
这样路径压缩有一个好处,就是时间复杂度明显降低.
所以,包含路径压缩的root函数就是:
int root(int u) const{
if (father[u]==u) return u; //若u的父亲是自己,u即为最老的祖先
else return father[u]=root(father[u]); //否则递归的查找u的父亲的祖先,顺便将father[i]赋值为最终的祖先
}这样就会让接下来的合并与查找时间复杂度变成O(log(n)).
我也不知道怎么算的.
这种森林支持合并的操作,但却不支持删除.
它是一棵树,但它这棵树的空间却只有O(n).
每个点i都表示一个节点,还有father[i]表示它的父节点.
它是一种用于判断两个点是否有关系的数据结构.
它的具体实现是这样的:
1.并查集用数组father来储存各个点的祖先.
2.一般并查集只有两个函数:初始化与查找一个点的祖先.
3.初始化:每个点在最开始的祖先都是自己,一般不特别写函数.
4.查找一个点的祖先:用root(u)代表查找x的祖先,它的思想就是不断递归地寻找当前点u的father,直至u的father是自己.
5.在root的基础上,可以再写一个合并的函数,思想为将y的祖先的父亲定为x.
6.查找x与y是否在一个家族里,就比较x与y的祖先是否相同
于是,并查集大概可以这样实现:
class forest{ int father[maxn],n; //维护一个储存每个点的父亲的数组以及一个n代表节点数 void clear(int x) const{ //将并查集清空后初始化为一个有x个点的森林 n=x; for (int i=1;i<=n;i++) father[i]=i; //将所有点的父亲初始化为自己 } int root(int u) const{ if (father[u]==u) return u; //若u的父亲是自己,u即为最老的祖先 else return root(father[u]); //否则递归的查找u的父亲的祖先 } void merge(int x,int y) const{ //在寻找一个点祖先的函数的基础上,加一个合并的函数 father[root(y)]=x; //将y的祖先的父亲定为x }
bool same(int x,int y) const{ //判断x与y是否在同一家族内 if (root(x)==root(y)) return true; //祖先相同,说明在同一家族内 else return false; //否则不在一个家族 }
};那这能做什么呢?比如说下面的例题:
每个人都有很多亲戚,而有时亲戚太多就不好认,于是就有人来提出一个问题,给出一些亲戚关系,之后让你判断x与y是否是亲戚.
这个问题可以用并查集完成,就将给出亲戚关系当做合并,判断亲戚关系当成判断是否在同一家族就行了.
每次合并最差时间复杂度O(n),每次查找时间复杂度O(n),慢得如同跑dfs回溯.
但其实我们可以进行优化,因为我们可以再递归时顺便把每个点的父节点直接改为祖先节点,这样做可行的理由是并查集不支持删除.
这样路径压缩有一个好处,就是时间复杂度明显降低.
所以,包含路径压缩的root函数就是:
int root(int u) const{
if (father[u]==u) return u; //若u的父亲是自己,u即为最老的祖先
else return father[u]=root(father[u]); //否则递归的查找u的父亲的祖先,顺便将father[i]赋值为最终的祖先
}这样就会让接下来的合并与查找时间复杂度变成O(log(n)).
我也不知道怎么算的.
相关文章推荐
- 亲戚 (并查集路径压缩)
- 并查集及其路径的压缩
- 算法 1.5节:带路径压缩的加权并查集
- 并查集--路径压缩
- 并查集算法和路径压缩
- hdu 3635 Dragon Balls ( 并查集路径压缩)
- SDUT 3386 小雷的冰茶几 并查集 压缩路径
- POJ 1984 Navigation Nightmare(路径压缩并查集)
- 并查集 路径压缩(具体解释)
- 【并查集+压缩路径】
- LA 3027 Corporative Network 合作网络【并查集+路径压缩】
- uva1493 - Draw a Mess 并查集路径压缩
- 并查集 带压缩路径的版本
- 并查集路径压缩
- 并查集 路径压缩 非递归写法
- 暑假集训 8.17 数据结构实验:连通分量个数(并查集判断连通分量个数 路径压缩)sdutoj1488
- 并查集的 路径压缩(递归和非递归)
- 并查集的路径压缩
- 并查集 按秩合并&路径压缩
- HDU 3635 Dragon Balls(并查集路径压缩)