Kruskal

  1. /**
  2. * 克鲁斯卡尔算法 K算法
  3. * 无向图,Kruskal算法生成最小生成树
  4. * 实现: 并查集+小根堆
  5. */
  6. //undirected graph only
  7. public class Code04_Kruskal {
  8. // Union-Find Set
  9. public static class UnionFind {
  10. // key 某一个节点, value key节点往上的节点
  11. private HashMap<Node, Node> fatherMap;
  12. // key 某一个集合的代表节点, value key所在集合的节点个数
  13. private HashMap<Node, Integer> sizeMap;
  14. public UnionFind() {
  15. fatherMap = new HashMap<Node, Node>();
  16. sizeMap = new HashMap<Node, Integer>();
  17. }
  18. // 将所有的点建成自己的集合,相当于初始化操作
  19. public void makeSets(Collection<Node> nodes) {
  20. fatherMap.clear();
  21. sizeMap.clear();
  22. for (Node node : nodes) {
  23. fatherMap.put(node, node);
  24. sizeMap.put(node, 1);
  25. }
  26. }
  27. // isSameSet
  28. public boolean isSameSet(Node a, Node b) {
  29. return findHead(a) == findHead(b);
  30. }
  31. // findHead
  32. private Node findHead(Node n) {
  33. Stack<Node> path = new Stack<>();
  34. while(n != fatherMap.get(n)) {
  35. path.add(n);
  36. n = fatherMap.get(n);
  37. }
  38. while(!path.isEmpty()) {
  39. fatherMap.put(path.pop(), n);
  40. }
  41. return n;
  42. }
  43. // union
  44. public void union(Node a, Node b) {
  45. if (a == null || b == null) {
  46. return;
  47. }
  48. Node aDai = findHead(a);
  49. Node bDai = findHead(b);
  50. if (aDai != bDai) {
  51. int aSetSize = sizeMap.get(aDai);
  52. int bSetSize = sizeMap.get(bDai);
  53. if (aSetSize <= bSetSize) {
  54. fatherMap.put(aDai, bDai);
  55. sizeMap.put(bDai, aSetSize + bSetSize);
  56. sizeMap.remove(aDai);
  57. } else {
  58. fatherMap.put(bDai, aDai);
  59. sizeMap.put(aDai, aSetSize + bSetSize);
  60. sizeMap.remove(bDai);
  61. }
  62. }
  63. }
  64. }
  65. // 比较器
  66. public static class EdgeComparator implements Comparator<Edge> {
  67. @Override
  68. public int compare(Edge o1, Edge o2) {
  69. return o1.weight - o2.weight;
  70. }
  71. }
  72. // 最小生成树
  73. public static Set<Edge> kruskalMST(Graph graph) {
  74. UnionFind unionFind = new UnionFind();
  75. // 把所有的点建成一个自己的集合
  76. unionFind.makeSets(graph.nodes.values());
  77. // 从小的边到大的边,依次弹出,小根堆! 贪心的过程
  78. PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(new EdgeComparator());
  79. for (Edge edge : graph.edges) { // M 条边
  80. priorityQueue.add(edge); // O(logM)
  81. }
  82. Set<Edge> result = new HashSet<>();
  83. while (!priorityQueue.isEmpty()) { // M 条边
  84. Edge edge = priorityQueue.poll(); // O(logM)
  85. // 如果当前边加入后不会形成环,则要进行边的收尾节点进行合并,否则不要
  86. if (!unionFind.isSameSet(edge.from, edge.to)) { // O(1)
  87. result.add(edge);
  88. unionFind.union(edge.from, edge.to);
  89. }
  90. }
  91. return result;
  92. }
  93. }

Prim

public class Code05_Prim {

    public static class EdgeComparator implements Comparator<Edge> {
        @Override
        public int compare(Edge o1, Edge o2) {
            return o1.weight - o2.weight;
        }
    }

    public static Set<Edge> primMST(Graph graph) {
        // 解锁的边进入小根堆
        PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(new EdgeComparator());
        // 哪些点被解锁出来了
        HashSet<Node> nodeSet = new HashSet<>();
        Set<Edge> result = new HashSet<>(); // 依次挑选的的边在result里
        for (Node node : graph.nodes.values()) { // 随便挑了一个点
            // node 是开始点
            if (!nodeSet.contains(node)) {
                nodeSet.add(node);
                for (Edge edge : node.edges) { // 由一个点,解锁所有相连的边
                    priorityQueue.add(edge);
                }
                while (!priorityQueue.isEmpty()) {
                    Edge edge = priorityQueue.poll(); // 弹出解锁的边中,最小的边
                    Node toNode = edge.to; // 可能的一个新的点
                    if (!nodeSet.contains(toNode)) { // 不含有的时候,就是新的点
                        nodeSet.add(toNode);
                        result.add(edge);
                        for (Edge nextEdge : toNode.edges) {
                            priorityQueue.add(nextEdge);
                        }
                    }
                }
            }
            // break;
        }
        return result;
    }

    // 请保证graph是连通图
    // graph[i][j]表示点i到点j的距离,如果是系统最大值代表无路
    // 返回值是最小连通图的路径之和
    public static int prim(int[][] graph) {
        int size = graph.length;
        int[] distances = new int[size];
        boolean[] visit = new boolean[size];
        visit[0] = true;
        for (int i = 0; i < size; i++) {
            distances[i] = graph[0][i];
        }
        int sum = 0;
        for (int i = 1; i < size; i++) {
            int minPath = Integer.MAX_VALUE;
            int minIndex = -1;
            for (int j = 0; j < size; j++) {
                if (!visit[j] && distances[j] < minPath) {
                    minPath = distances[j];
                    minIndex = j;
                }
            }
            if (minIndex == -1) {
                return sum;
            }
            visit[minIndex] = true;
            sum += minPath;
            for (int j = 0; j < size; j++) {
                if (!visit[j] && distances[j] > graph[minIndex][j]) {
                    distances[j] = graph[minIndex][j];
                }
            }
        }
        return sum;
    }

}