什么是并查集
并查集是一种数据结构,用于处理不相交集合的合并及查询问题。它是一种树型数据结构,每个节点代表一个集合,节点中存储该集合的所有元素,且每个节点指向它的子节点,按照元素的大小关系构成一个有向图。
在并查集中,查找操作可以在常数时间内完成,而合并操作则需要使用一定的算法进行优化,通常是使用哈希表来实现哈希冲突解决,以减少时间复杂度。
并查集在数据挖掘、图论等领域有广泛的应用,例如用于构建关联规则挖掘算法、社交网络分析等。
功能原理
在并查集里,我们要实现几个功能.
1.两个元素在不在同一个集合里.
2.当前集合的大小
3.把两个元素合并在一个集合里.
实现步骤:
A.我们在初始化并查集时,每个元素就是一个单独的集合,他的大小就是1.
B.判断两个元素是不是在同一个集合,是要判断两个元素的头节点或者是不是同一个节点,如果在同一个节点,就认定是同一个集合中,
C.合并两个集合时,我们先判断两个集合的大小,小的挂载在大的下面.
HashMap 实现并查集
/**
* 对每个元素封装下
* @param <V>
*/
public static class Node<V> {
V value;
public Node(V v) {
value = v;
}
}
/**
* 用map 来实现并查集
* @param <V>
*/
public static class UnionFind<V> {
public HashMap<V, Node<V>> nodes;
//用map集合来代表指针,
public HashMap<Node<V>, Node<V>> parents;
public HashMap<Node<V>, Integer> sizeMap;
public UnionFind(List<V> values) {
nodes = new HashMap<>();
parents = new HashMap<>();
sizeMap = new HashMap<>();
for (V cur : values) {
Node<V> node = new Node<>(cur);
nodes.put(cur, node);
parents.put(node, node);
sizeMap.put(node, 1);
}
}
// 给你一个节点,请你往上到不能再往上,把代表返回
public Node<V> findFather(Node<V> cur) {
Stack<Node<V>> path = new Stack<>();
while (cur != parents.get(cur)) {
path.push(cur);
cur = parents.get(cur);
}
//每次查询时 优化下结构,子节点直接挂载在头节点下
while (!path.isEmpty()) {
parents.put(path.pop(), cur);
}
return cur;
}
public boolean isSameSet(V a, V b) {
return findFather(nodes.get(a)) == findFather(nodes.get(b));
}
/**
* 合并两个节点
* @param a
* @param b
*/
public void union(V a, V b) {
Node<V> aHead = findFather(nodes.get(a));
Node<V> bHead = findFather(nodes.get(b));
//不在同一个集合时 才去合并
if (aHead != bHead) {
//获取大小,小的挂载在大的下面
int aSetSize = sizeMap.get(aHead);
int bSetSize = sizeMap.get(bHead);
Node<V> big = aSetSize >= bSetSize ? aHead : bHead;
Node<V> small = big == aHead ? bHead : aHead;
parents.put(small, big);
sizeMap.put(big, aSetSize + bSetSize);
//挂载后,删除掉大小记录
sizeMap.remove(small);
}
}
/**
* 集合的个数
* @return
*/
public int sets() {
return sizeMap.size();
}
}
用数组实现并查集(效率更高)
public class UnionFind {
public static int[] father = new int[10000];
public static int[] size = new int[10000];
public static int[] help = new int[10000];
// 初始化并查集
public static void init(int n) {
for (int i = 0; i <= n; i++) {
father[i] = i;
size[i] = 1;
}
}
// 从i开始寻找集合代表点
public static int find(int i) {
int hi = 0;
while (i != father[i]) {
help[hi++] = i;
i = father[i];
}
//优化结构
for (hi--; hi >= 0; hi--) {
father[help[hi]] = i;
}
return i;
}
// 查询x和y是不是一个集合
public static boolean isSameSet(int x, int y) {
return find(x) == find(y);
}
// x所在的集合,和y所在的集合,合并成一个集合
public static void union(int x, int y) {
int fx = find(x);
int fy = find(y);
if (fx != fy) {
if (size[fx] >= size[fy]) {
size[fx] += size[fy];
father[fy] = fx;
} else {
size[fy] += size[fx];
father[fx] = fy;
}
}
}
}