并查集: 并查集主要有两个作用。1:它可以查询两个元素A和B是不是同一个集合里面的。2:它可以把A和B两个元素所在的集合合并起来,注意是两个元素所在的集合,而不是两个元素。使用并查集的时候要一次性把数据全部输入,不支持动态的变化。
下面来说一下实现并查集的结构:
使用这样一种方式,假设每一个元素都是自己的代表节点,用代表节点表示集合。
合并时:那个集合的元素更少,就挂在元素多的那个集合下面
查找时:两个元素不断往上找,如果两个元素的代表节点一样,那么则是在同一个集合上。
并查集的优化:查询元素的时候,如果查到这个元素,则把这个元素之前的元素全部打平,即直接放到代表节点的下面。
代码:
import java.util.HashMap;
import java.util.List;
public class Code_04_UnionFind {
public static class Node {
}
public static class UnionFindSet {
public HashMap<Node, Node> fatherMap; // key:child value: father
public HashMap<Node, Integer> sizeMap;// 这个代表节点集合一共有多少个节点
public UnionFindSet() {
makeSets(nodes);
}
private void makeSets(List<Node> nodes) { //初始化并查集 一开始要把所有的数据都传进来
fatherMap = new HashMap<Node, Node>();
sizeMap = new HashMap<Node, Integer>();
for (Node node : nodes) {
fatherMap.put(node, node); //每一个节点都是一个集合,都是代表节点
sizeMap.put(node, 1); //长度都是1
}
}
private Node findHead(Node node) { //找到代表节点,并完成那个优化的功能
Node father = fatherMap.get(node);
if (father != node) {
father = findHead(father);
}
fatherMap.put(node, father);
return father; //把最上面的那个代表节点返回
}
public boolean isSameSet(Node a, Node b) {
return findHead(a) == findHead(b); //看两个代表节点是不是一样
}
public void union(Node a, Node b) {
if (a == null || b == null) {
return;
}
Node aHead = findHead(a);
Node bHead = findHead(b);
if (aHead != bHead) {
int aSetSize= sizeMap.get(aHead); //分别得到a和b集合的长度
int bSetSize = sizeMap.get(bHead);
if (aSetSize <= bSetSize) { //如果a的长度小于b,那么把a放在b集合的下面.反之,放在a的下面
fatherMap.put(aHead, bHead);
sizeMap.put(bHead, aSetSize + bSetSize);
} else {
fatherMap.put(bHead, aHead);
sizeMap.put(aHead, aSetSize + bSetSize);
}
}
}
}
public static void main(String[] args) {
}
}
当查询次数和合并次数的复杂度逼近O(n)的时候,可以认为单次的查询的复杂度是O(1)的。