并查集这种结构是为了以常量的时间复杂度解决这样的问题:
- 判断两个节点是否在同一个集合
- 如何合并两个集合
结构
public class UnionFindSet<V> {//并查集节点public static class Node<V> {//当前节点的值public V value;//上一个节点public Node<V> pre;//这个节点作为集合的代表节点(head),集合中的节点个数public int size;public Node(V v) {this.value = value;this.pre = this;this.size = 1;}}//所有元素的Mappublic HashMap<V,Node<V>> elementMap;public UnionFindSet(Collection<V> collection) {this.elementMap = new HashMap<>();for (V v : collection) {Node node = new Node(v);this.elementMap.put(v,node);}}//找到节点所在集合的代表节点(头节点)public Node<V> findHead(Node<V> element) {Node node = element;//扁平优化Stack<Node<V>> stack = new Stack<>();while (node != node.pre) {stack.push(node);node = node.pre;}//扁平优化while (!stack.isEmpty()) {stack.pop().pre = node;}return node;}//查看两个节点是否在同一个集合中public boolean isSameSet(V a,V b) {if (elementMap.containsKey(a) && elementMap.containsKey(b)) {return findHead(elementMap.get(a)) == findHead(elementMap.get(b));} else {return false;}}//合并集合public void union(V a,V b) {if (elementMap.containsKey(a) && elementMap.containsKey(b)) {Node<V> headA = findHead(elementMap.get(a));Node<V> headB = findHead(elementMap.get(b));if (headA != headB) {//找到节点数更多的集合Node<V> big = headA.size >= headB.size ? headA : headB;Node<V> small = big == headA ? headB : headA;//让small集合挂载到big集合上small.pre = big;big.size += small.size;small.size = 0 ;}}}}
并查集实现克鲁斯卡尔算法
public static Set<Edge> kruskal(Graph graph) {Set<Edge> ans = new HashSet<>();UnionFindSet<Node> unionFindSet = new UnionFindSet<Node>(graph.nodes.values());PriorityQueue<Edge> queue = new PriorityQueue<>(new Comparator<Edge>() {@Overridepublic int compare(Edge o1, Edge o2) {return o1.weight - o2.weight;}});for (Edge edge : graph.edges) {queue.offer(edge);}while (!queue.isEmpty()) {Edge edge = queue.poll();Node from = edge.from;Node to = edge.to;if (!unionFindSet.isSameSet(from,to)) {ans.add(edge);unionFindSet.union(from,to);}}return ans;}
