“并查集”是一种常见的数据结构,在互联网大厂的笔试题中也能看到它的身影。在刷LeetCode每日一题过程中,发现这种数据结构的妙处,参考LeetCode官方题解,写了这篇博文,供各位小伙伴们学习使用。
并查集的相关知识
- 并查集用于判断一对元素是否相连,它们的关系是动态添加的,这一类问题叫做【动态连通性】问题;
- 支持【合并】与【查询是否在同一集合】的操作;
- 底层结构为【数组】或者【哈希表】,用于表示【结点】指向【父结点】,初始化时指向自己;
- 【合并】就是把一个集合的根结点指向另一个集合带的根结点,只要根结点一样,就表示在同一个集合里。
- 这种表示【不相交集合】的方法称之为【代表元法】,以每个结点的根结点作为一个集合的【代表元】。
并查集的相关应用
- 最小生产树
- kruskal算法
并查集的优化-路径压缩(隔代压缩)
只要在原始代码的基础上加上下面的代码:
parent[x]=parent[parent[x]];
并查集的优化-路径压缩(完全压缩)
需要借助递归方法
并查集的优化-按秩合并
- 按秩合并是指在合并时,使得【高度】更低树的根结点指向【高度】更高树的根结点,避免合并后的树高度增加;
- 【路径压缩】和【按秩合并】一起使用时,难以维护秩的准确定义,但依然具有一定的参考价值。
LeetCode990题目如下:
给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 equations[i] 的长度为 4,并采用两种不同的形式之一:“a==b” 或 “a!=b”。在这里,a 和 b 是小写字母(不一定不同),表示单字母变量名。
只有当可以将整数分配给变量名,以便满足所有给定的方程时才返回 true,否则返回 false。
示例 1:
输入:[“a==b”,“b!=a”]
输出:false
解释:如果我们指定,a = 1 且 b = 1,那么可以满足第一个方程,但无法满足第二个方程。没有办法分配变量同时满足这两个方程。
示例 2:
输出:[“ba","ab”]
输入:true
解释:我们可以指定 a = 1 且 b = 1 以满足满足这两个方程。
示例 3:
输入:[“ab","bc”,“a==c”]
输出:true
示例 4:
输入:[“ab",“b!=c”,"ca”]
输出:false
示例 5:
输入:[“cc","bd”,“x!=z”]
输出:true
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/satisfiability-of-equality-equations
思路:
- 由于相等关系具有传递性,所有相等的变量属于同一个集合;
- 只关心连通性,不关心具体节点之间的距离,因此可以使用并查集这种数据结构。
算法设计流程
- 扫描所有等式,将等式两边1的顶点进行合并;
- 再扫描所有不等式,检查每一个不等式的两个顶点是不是在一个连通分量里面;
- 如果在,返回false,表示等式方程有矛盾;
- 否则,返回true。
Java题解代码如下:
class Solution {
public static void main(String[] args) {
String[] equations={"a==b","b!=c","a==c"};
System.out.println(equationsPossible(equations));
}
public static boolean equationsPossible(String[] equations) {
int length = equations.length;
int[] parent = new int[26];
for (int i = 0; i < 26; i++) {
parent[i] = i;
}
for (String str : equations) {
if (str.charAt(1) == '=') {
int index1 = str.charAt(0) - 'a';
int index2 = str.charAt(3) - 'a';
union(parent, index1, index2);
}
}
for (String str : equations) {
if (str.charAt(1) == '!') {
int index1 = str.charAt(0) - 'a';
int index2 = str.charAt(3) - 'a';
if (find(parent, index1) == find(parent, index2)) {
return false;
}
}
}
return true;
}
public static void union(int[] parent, int index1, int index2) {
parent[find(parent, index1)] = find(parent, index2);
}
public static int find(int[] parent, int index) {
while (parent[index] != index) {
parent[index] = parent[parent[index]];
index = parent[index];
}
return index;
}
}