十三、并查集
并查集的作用:
- 可以回答连接问题。
- 判断网络中结点间的连接状态。
- 可以实现数学中的集合。
- 连接问题和路径问题。连接问题比路径问题回答的问题要少。
- 和二分查找法作比较。
- 和 select 作比较。
- 和堆作比较。
- 判断一个图中有没有环,可以用并查集作为辅助的数据结构。
优化策略:[数据压缩]和[按秩合并]
(一)数据压缩:隔代压缩与完全压缩
- 隔代压缩性能比较高,虽然压缩不完全,不过多次执行隔代压缩也能达到完全压缩的效果,通常大佬偏向于隔代压缩;
- 完全压缩需要借助系统栈,使用递归的写法。或者先找到当前结点的根结点,然后把沿途上所有的结点都指向根结点,得遍历两次。
(二)按秩合并
秩也有两种含义:
秩表示以当前结点为根结点的子树结点总数,即这里的「秩」表示 size 含义; 秩表示以当前结点为根结点的子树的高度,即这里的「秩」表示 rank 含义(更合理,因为查询时候的时间性能主要决定于树的高度)。 注:如果同时使用「路径压缩」与「按秩合并」,这里的「秩」就失去了它的定义,但即使秩表示的含义不准确,也能够作为合并时候很好的「参考」。在这种情况下,并查集的查询与合并的时间复杂度可以达到接近 O(1)。
990. 等式方程的可满足性[1]
class Solution {
public boolean equationsPossible(String[] equations) {
UnionFind unionFind=new UnionFind(26);
for(String equation:equations){
char[]charArray=equation.toCharArray();
if(charArray[1]=='='){ int index1=charArray[0]-'a'; int index2=charArray[3]-'a'; unionFind.union(index1,index2); } } for(String equation:equations){ char[]charArray=equation.toCharArray(); if(charArray[1]=='!'){ int index1=charArray[0]-'a'; int index2=charArray[3]-'a'; if(unionFind.isConnection(index1,index2)){ return false; } } } return true; } private class UnionFind{ private int []parent; //构造方法 public UnionFind(int n){ parent=new int [n]; for(int i=0;i<n;i++){ parent[i]=i; } } //查找 public int find(int x){ while(x!=parent[x]){ parent[x]=parent[parent[x]]; x=parent[x]; } return x; } //合并 public void union(int x,int y){ int rootX=find(x); int rootY=find(y); parent[rootX]=rootY; } //判断是否连接 public boolean isConnection(int x,int y){ return find(x)==find(y); } } }
684. 冗余连接[2]
class Solution {
public int[] findRedundantConnection(int[][] edges) {
int len=edges.length;
UnionFind unionFind=new UnionFind(len+1);
for(int[] edge:edges){
if(unionFind.isConnection(edge[0],edge[1])){ return edge; } unionFind.union(edge[0],edge[1]); } return new int[0]; } //并查集 private class UnionFind{ private int []parent; //构造方法 public UnionFind(int n){ parent=new int [n]; for(int i=0;i<n;i++){ parent[i]=i; } } //查找 public int find(int x){ while(x!=parent[x]){ parent[x]=parent[parent[x]]; x=parent[x]; } return x; } //合并 public void union(int x,int y){ int rootX=find(x); int rootY=find(y); parent[rootX]=rootY; } //判断是否连接 public boolean isConnection(int x,int y){ return find(x)==find(y); } } }
参考资料
[1]
990、 等式方程的可满足性: https://leetcode-cn.com/problems/satisfiability-of-equality-equations/
[2]
684、 冗余连接: https://leetcode-cn.com/problems/redundant-connection/