您的位置:首页 > 其它

并查集的基础及其路径压缩(亲戚)

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的祖先是否相同
于是,并查集大概可以这样实现:
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)).
我也不知道怎么算的.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: