并查集:支持两个集合合并,两个是否一个集合。让两个操作几乎都是O(1)<br /> 如果使用链表 合并是O(1) 查询是否一个集合就不是了,使用hash表 查询是O(1),合并就不是了。<br />使用往上值的图的方式:<br /> 看两个是否一个集合:指针一直往上指,指到不能再指了,看是否同一个代表元素,是就是同一集合,否则不是。<br /> 两个集合合并:当两个不是集合的情况都是自己指自己,改变某个顶指针,指向另外一个。 <br /> 当两个是集合时候,数量少的顶部连向数量多的顶部。<br /> 关键是往上指到不能再指了,这个过程如果链条过长会达到瓶颈,所以当指到不能再指的时候将沿途的节点都指向最上面的。
// 样本会进来包一层,叫做元素public static class Node<V> {V value;public Node(V v) {value = v;}}// 并查集结构public static class UnionFind<V> {// 包一层 a对应a圈public HashMap<V, Node<V>> nodes;// key 某个元素,value指该元素的父 。 小弟 大哥public HashMap<Node<V>, Node<V>> parents;// key 某个集合的代表元素 ,value 该集合的大小,包含一个顶点和有多少小弟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> findHead(Node<V> cur) {Stack<Node<V>> path = new Stack<>();// 不是顶点的时候while (cur != parents.get(cur)) {// 沿途节点加入栈path.push(cur);// 获得最顶点cur = parents.get(cur);}while (!path.isEmpty()) {// 将这条链上的father全都改成cur,扁平化parents.put(path.pop(), cur);}return cur;}// 是否一个集合public boolean isSameSet(V a, V b) {// 是否初始化if(nodes.containsKey(a)&& nodes.containsKey(b)){// 是不是同一个顶点return findHead(nodes.get(a)) == findHead(nodes.get(b));}return false;}// 合并public void union(V a, V b) {if(nodes.containsKey(a)&& nodes.containsKey(b)){// 找到各自顶点Node<V> aHead = findHead(nodes.get(a));Node<V> bHead = findHead(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);}}}public int sets() {return sizeMap.size();}}
