什么是并查集
并查集是一种数据结构,用来快速查询集合元素之间是否有关系,是否有关系判断标准是是否有相同的根节点
举一个恰当的例子,要判断图谱中的两个元素是否有关系,如果使用常规的查询方法,时间复杂度比较大,使用并查集就是用来优化这种情况,使得判断两个元素是否有关联可以达到O(1)
普通查询思路
按照上图,要判断11和4是否有关系,我们可以一直向上追溯父亲节点,11的上层是9,9的上层节点是0,那么11的祖父节点就是0。同理,追溯4的祖父节点是0,那么这两个节点的祖父节点都是0,则两个节点有关系
知道了思路,抽象出代码就很简单了,现在要解决的有两个问题,使用非递归,从下往上进行追溯,什么时候是截止条件呢,可以让0号节点的父节点指向自己。第二个问题就是如何存储这样一个结构,由于每个节点只需要用到父节点,可以使用map来进行存储,代码如下:
//找a的祖父节点
int father = map.get(a);
while (father != map.get(father)) {
father = map.get(father);
}
O(1)优化
按照上面的思路一级一级的追溯,很明显时间复杂度是O(n),如何进行优化呢
举个例子,当查询过一遍4的祖父节点是0的时候,我们就可以直接更新4的父节点是0,后续再使用到4的时候,就不会做重新劳动了,我们可以继续思考,既然用到4了,我们可以直接将4开始往上所有的路径节点都直接更新
所以,整个思路分为两步,第一步还是先找到顶级根节点,第二步从当前节点开始往上,依次更新父节点
//找a的祖父节点
int father = map.get(a);
while (father != map.get(father)) {
father = map.get(father);
}
//从当前节点往上更新父节点
int temp = -1;
int fa = map.get(a);
while(fa != map.get(fa)) {
//temp保存原父节点,防止更新后丢失
temp = map.get(fa);
map.put(fa,father);
fa = temp;
}
数据结构
根据上面的思路,就可以整理出来该数据结构
class UnionFind {
HashMap<Integer,Integer> map;
public UnionFind() {
map = new HashMap<>();
}
//初始化所有节点,每个节点的父节点都指向自己
public void find0() {
//... 这里根据情况来写,遍历每个父节点指向自己
}
public int find(int num) {
int father = map.get(num);
while (father != map.get(father)) {
father = map.get(father);
}
//从当前节点往上更新父节点
int temp = -1;
int fa = map.get(num);
while(fa != map.get(fa)) {
//temp保存原父节点,防止更新后丢失
temp = map.get(fa);
map.put(fa,father);
fa = temp;
}
return father;
}
//合并
public void union(int a,int b) {
int fa_a = find(a);
int fa_b = find(b);
if(fa_a != fa_b) {
map.put(fa_a,fa_b);
}
}
}