基本实现
任务:
维护多个不相交的集合,支持两种操作:合并两个集合,查询一个元素所在的集合。
说明:
维护一个森林,每一棵树都代表一个集合,树根元素为这个集合的代表元。利用数组father[]查询记录每个元素的父亲节点。
查询一个元素所处集合时,只需不断寻找父节点,即可找到该元素所处集合的代表元。
合并两个集合时,先找到两个集合代表元x,y,然后令father[x]=y即可。
优化:
路径压缩,沿着树根的路径找到元素a所在集合代表元b后,对这条路径上的所有元素x,令father[a]=b;
按rank启发式合并,对于每个集合维护一个rank值,每次将rank较小的集合合并到rank较大的集合,合并两个相同的集合时rank=rank+1。
根据不同情况也可以添加集合大小等属性。
public class union_find {
private int[] father;
private int[] rank;
//初始化
public union_find(int n) {
father = new int[n];
rank = new int[n];
for (int i = 0; i < n; i++) {
father[i] = i;
rank[i] = 1;
}
}
//查询在那个集合
int find(int v) {
return father[v] = father[v] == v ? v : find(father[v]);
}
//合并
void merge(int x, int y) {
int a = find(x);
int b = find(y);
if (rank[a] < rank[b]) {
father[a] = b;
} else {
father[b] = a;
if (rank[a] == rank[b]) {
rank[a]++;
}
}
}
}
例题
省份数量https://leetcode.cn/problems/number-of-provinces/description/代码:
class Solution {
public int findCircleNum(int[][] isConnected) {
if(isConnected==null||isConnected.length == 0){
return 0;
}
int n=isConnected.length;
union_find uf=new union_find(n);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if(isConnected[i][j]==1){
uf.merge(i,j);
}
}
}
int cnt=0;
for (int i = 0; i < n; i++) {
if(uf.father[i]==i){
cnt++;
}
}
return cnt;
}
public class union_find {
private int[] father;
private int[] rank;
//初始化
public union_find(int n) {
father = new int[n];
rank = new int[n];
for (int i = 0; i < n; i++) {
father[i] = i;
rank[i] = 1;
}
}
//查询在那个集合
int find(int v) {
return father[v] = father[v] == v ? v : find(father[v]);
}
//合并
void merge(int x, int y) {
int a = find(x);
int b = find(y);
if (rank[a] < rank[b]) {
father[a] = b;
} else {
father[b] = a;
if (rank[a] == rank[b]) {
rank[a]++;
}
}
}
}
}
剑指 Offer II 119. 最长连续序列https://leetcode.cn/problems/WhsWhI/description/
代码:
import java.util.HashMap;
class Solution {
public int longestConsecutive(int[] nums) {
//存储下标和值
Map<Integer,Integer>map=new HashMap<>();
union_find uf=new union_find(nums.length);
for (int i = 0; i < nums.length; i++) {
if(map.containsKey(nums[i]))continue;
if(map.containsKey(nums[i]-1)){
uf.merge(i,map.get(nums[i]-1));
}
if(map.containsKey(nums[i]+1)){
uf.merge(i,map.get(nums[i] + 1));
}
map.put(nums[i], i);
}
int size=0;
for (int i = 0; i < nums.length; i++) {
if(uf.father[i]==i){
size=Math.max(size,uf.size[i]);
}
}
return size;
}
public class union_find {
private int[] father;
private int[] size;
//初始化
public union_find(int n) {
father = new int[n];
size= new int[n];
for (int i = 0; i < n; i++) {
father[i] = i;
size[i] = 1;
}
}
//查询在那个集合
int find(int v) {
return father[v] = father[v] == v ? v : find(father[v]);
}
//合并
void merge(int x, int y) {
int a = find(x);
int b = find(y);
if(a==b)return;
father[a]=b;
size[b]+=size[a];
}
}
}