1. 定义一个并查集的类,并实现基本操作:
class UnionFind{
// 用Map存储并查集,表达的含义是key的父节点是value
private Map<Integer,Integer> father;
// 0.构造函数初始化
public UnionFind(int n){
father = new HashMap<Integer,Integer>();
for(int i = 0;i < n;i++){
father.put(i,null);
}
}
// 1.添加:初始加入时,每个元素都是一个独立的集合,因此
public void add(int x){ // 根节点的父节点为null
if(!father.containesKey(x)){
father.put(x,null)
}
}
// 2. 查找:反复查找父节点
public int findFather(){
int root = x;
while(father.get(root)){
root = father.get(root)
}
while(x != root){ // 路径压缩,把x到root上所有节点都挂到root下面
int original_father = father.get(x); // 保存原来的父节点
father.put(x,root) // 当前节点挂到根节点下面
x = original_father; // x 赋值为原来的父节点继续执行刚刚的操作
}
return root;
}
// 3.合并:把两个集合合并成一个,只需要把其中一个集合的根节点挂到另一个集合的根节点下方
public void union(int x,int y){ // x的集合与y的集合合并
int rootX = find(x)
int rootY = find(y)
if(rootX != rootY){ // 节点联通只需要一个共同祖先,无所谓谁是根节点
father.put(rootX,rootY)
}
}
// 4. 判断:判断两个元素是否同属于一个集合
public boolean isConnected(int x,int y){
return find(x) == find(y)
}
}
  1. 练习:​​LeetCode 128. 最长连续序列​使用并查集实现:
// 大致思路:
/** 1.遍历所有元素num,如果num + 1存在,将num加入到num + 1所在的连通分量中;
2.重新遍历一遍所有元素num,通过find函数找到num所在分量的根节点,即最远右边界,从而求得连续区间的长度。
*/
class UnionFind{
// 记录每个节点的父节点
private Map<Integer,Integer> parent;
public UnionFind(int[] nums){
parent = new HashMap<>();
// 初始化父节点为自身
for(int num : nums){
parent.put(num,num)
}
}
// 寻找x的父节点,实际上也就是x的最远连续右边界
public Integer find(int x){
// nums不包含x
if(!parent.containsKey(x)){
return null;
}
// 遍历找到x的父节点
while(x != parent.get(x)){
// 路径压缩
parent.put(x,parent.get(parent.get(x)));
x = parent.get(x)
}
return x;
}
// 合并两个连通分量(用来将num并入到num+1的连续区间中)
public void union(int x,int y){
int rootX = find(x)
int rootY = find(y)
if(rootX == rootY){
return;
}
parent.put(rootX,rootY)
}
}
class Solution{
public int longestConsecutive(int[] nums){
UnionFind uf = new UnionFind(nums);
int ans = 0;
for(int num : nums){
// 当num+1存在,将num合并到num+1所在集合中
if(uf.find(num + 1) != null){
uf.union(num,num + 1)
}
}
for(int num : nums){
// 找到num的最远连续右边界
int right = uf.find(num)
ans = Math.max(ans,right - num + 1)
}
return ans;
}
}