| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent ace84ab commit af4ca77
2 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,113 @@ | |||
| 1 | + // KRUSKAL'S ALGORITHM | ||
| 2 | + // https://cp-algorithms.com/data_structures/disjoint_set_union.html | ||
| 3 | + // https://cp-algorithms.com/graph/mst_kruskal_with_dsu.html | ||
| 4 | + | ||
| 5 | + package graph | ||
| 6 | + | ||
| 7 | + import ( | ||
| 8 | + "sort" | ||
| 9 | + ) | ||
| 10 | + | ||
| 11 | + type Vertex int | ||
| 12 | + | ||
| 13 | + // Edge describes the edge of a weighted graph | ||
| 14 | + type Edge struct { | ||
| 15 | + Start Vertex | ||
| 16 | + End Vertex | ||
| 17 | + Weight int | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + // DisjointSetUnionElement describes what an element of DSU looks like | ||
| 21 | + type DisjointSetUnionElement struct { | ||
| 22 | + Parent Vertex | ||
| 23 | + Rank int | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + // DisjointSetUnion is a data structure that treats its elements as separate sets | ||
| 27 | + // and provides fast operations for set creation, merging sets, and finding the parent | ||
| 28 | + // of the given element of a set. | ||
| 29 | + type DisjointSetUnion []DisjointSetUnionElement | ||
| 30 | + | ||
| 31 | + // NewDSU will return an initialised DSU using the value of n | ||
| 32 | + // which will be treated as the number of elements out of which | ||
| 33 | + // the DSU is being made | ||
| 34 | + func NewDSU(n int) *DisjointSetUnion { | ||
| 35 | + | ||
| 36 | + dsu := DisjointSetUnion(make([]DisjointSetUnionElement, n)) | ||
| 37 | + return &dsu | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + // MakeSet will create a set in the DSU for the given node | ||
| 41 | + func (dsu DisjointSetUnion) MakeSet(node Vertex) { | ||
| 42 | + | ||
| 43 | + dsu[node].Parent = node | ||
| 44 | + dsu[node].Rank = 0 | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + // FindSetRepresentative will return the parent element of the set the given node | ||
| 48 | + // belongs to. Since every single element in the path from node to parent | ||
| 49 | + // has the same parent, we store the parent value for each element in the | ||
| 50 | + // path. This reduces consequent function calls and helps in going from O(n) | ||
| 51 | + // to O(log n). This is known as path compression technique. | ||
| 52 | + func (dsu DisjointSetUnion) FindSetRepresentative(node Vertex) Vertex { | ||
| 53 | + | ||
| 54 | + if node == dsu[node].Parent { | ||
| 55 | + return node | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + dsu[node].Parent = dsu.FindSetRepresentative(dsu[node].Parent) | ||
| 59 | + return dsu[node].Parent | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + // unionSets will merge two given sets. The naive implementation of this | ||
| 63 | + // always combines the secondNode's tree with the firstNode's tree. This can lead | ||
| 64 | + // to creation of trees of length O(n) so we optimize by attaching the node with | ||
| 65 | + // smaller rank to the node with bigger rank. Rank represents the upper bound depth of the tree. | ||
| 66 | + func (dsu DisjointSetUnion) UnionSets(firstNode Vertex, secondNode Vertex) { | ||
| 67 | + | ||
| 68 | + firstNode = dsu.FindSetRepresentative(firstNode) | ||
| 69 | + secondNode = dsu.FindSetRepresentative(secondNode) | ||
| 70 | + | ||
| 71 | + if firstNode != secondNode { | ||
| 72 | + | ||
| 73 | + if dsu[firstNode].Rank < dsu[secondNode].Rank { | ||
| 74 | + firstNode, secondNode = secondNode, firstNode | ||
| 75 | + } | ||
| 76 | + dsu[secondNode].Parent = firstNode | ||
| 77 | + | ||
| 78 | + if dsu[firstNode].Rank == dsu[secondNode].Rank { | ||
| 79 | + dsu[firstNode].Rank++ | ||
| 80 | + } | ||
| 81 | + } | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + // KruskalMST will return a minimum spanning tree along with its total cost | ||
| 85 | + // to using Kruskal's algorithm. Time complexity is O(m * log (n)) where m is | ||
| 86 | + // the number of edges in the graph and n is number of nodes in it. | ||
| 87 | + func KruskalMST(n int, edges []Edge) ([]Edge, int) { | ||
| 88 | + | ||
| 89 | + var mst []Edge // The resultant minimum spanning tree | ||
| 90 | + var cost int = 0 | ||
| 91 | + | ||
| 92 | + dsu := NewDSU(n) | ||
| 93 | + | ||
| 94 | + for i := 0; i < n; i++ { | ||
| 95 | + dsu.MakeSet(Vertex(i)) | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + sort.SliceStable(edges, func(i, j int) bool { | ||
| 99 | + return edges[i].Weight < edges[j].Weight | ||
| 100 | + }) | ||
| 101 | + | ||
| 102 | + for _, edge := range edges { | ||
| 103 | + | ||
| 104 | + if dsu.FindSetRepresentative(edge.Start) != dsu.FindSetRepresentative(edge.End) { | ||
| 105 | + | ||
| 106 | + mst = append(mst, edge) | ||
| 107 | + cost += edge.Weight | ||
| 108 | + dsu.UnionSets(edge.Start, edge.End) | ||
| 109 | + } | ||
| 110 | + } | ||
| 111 | + | ||
| 112 | + return mst, cost | ||
| 113 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,162 @@ | |||
| 1 | + package graph | ||
| 2 | + | ||
| 3 | + import ( | ||
| 4 | + "fmt" | ||
| 5 | + "testing" | ||
| 6 | + ) | ||
| 7 | + | ||
| 8 | + func Test_KruskalMST(t *testing.T) { | ||
| 9 | + | ||
| 10 | + var testCases = []struct { | ||
| 11 | + n int | ||
| 12 | + graph []Edge | ||
| 13 | + cost int | ||
| 14 | + }{ | ||
| 15 | + { | ||
| 16 | + n: 5, | ||
| 17 | + graph: []Edge{ | ||
| 18 | + { | ||
| 19 | + Start: 0, | ||
| 20 | + End: 1, | ||
| 21 | + Weight: 4, | ||
| 22 | + }, | ||
| 23 | + { | ||
| 24 | + Start: 0, | ||
| 25 | + End: 2, | ||
| 26 | + Weight: 13, | ||
| 27 | + }, | ||
| 28 | + { | ||
| 29 | + Start: 0, | ||
| 30 | + End: 3, | ||
| 31 | + Weight: 7, | ||
| 32 | + }, | ||
| 33 | + { | ||
| 34 | + Start: 0, | ||
| 35 | + End: 4, | ||
| 36 | + Weight: 7, | ||
| 37 | + }, | ||
| 38 | + { | ||
| 39 | + Start: 1, | ||
| 40 | + End: 2, | ||
| 41 | + Weight: 9, | ||
| 42 | + }, | ||
| 43 | + { | ||
| 44 | + Start: 1, | ||
| 45 | + End: 3, | ||
| 46 | + Weight: 3, | ||
| 47 | + }, | ||
| 48 | + { | ||
| 49 | + Start: 1, | ||
| 50 | + End: 4, | ||
| 51 | + Weight: 7, | ||
| 52 | + }, | ||
| 53 | + { | ||
| 54 | + Start: 2, | ||
| 55 | + End: 3, | ||
| 56 | + Weight: 10, | ||
| 57 | + }, | ||
| 58 | + { | ||
| 59 | + Start: 2, | ||
| 60 | + End: 4, | ||
| 61 | + Weight: 14, | ||
| 62 | + }, | ||
| 63 | + { | ||
| 64 | + Start: 3, | ||
| 65 | + End: 4, | ||
| 66 | + Weight: 4, | ||
| 67 | + }, | ||
| 68 | + }, | ||
| 69 | + cost: 20, | ||
| 70 | + }, | ||
| 71 | + { | ||
| 72 | + n: 3, | ||
| 73 | + graph: []Edge{ | ||
| 74 | + { | ||
| 75 | + Start: 0, | ||
| 76 | + End: 1, | ||
| 77 | + Weight: 12, | ||
| 78 | + }, | ||
| 79 | + { | ||
| 80 | + Start: 0, | ||
| 81 | + End: 2, | ||
| 82 | + Weight: 18, | ||
| 83 | + }, | ||
| 84 | + { | ||
| 85 | + Start: 1, | ||
| 86 | + End: 2, | ||
| 87 | + Weight: 6, | ||
| 88 | + }, | ||
| 89 | + }, | ||
| 90 | + cost: 18, | ||
| 91 | + }, | ||
| 92 | + { | ||
| 93 | + n: 4, | ||
| 94 | + graph: []Edge{ | ||
| 95 | + { | ||
| 96 | + Start: 0, | ||
| 97 | + End: 1, | ||
| 98 | + Weight: 2, | ||
| 99 | + }, | ||
| 100 | + { | ||
| 101 | + Start: 0, | ||
| 102 | + End: 2, | ||
| 103 | + Weight: 1, | ||
| 104 | + }, | ||
| 105 | + { | ||
| 106 | + Start: 0, | ||
| 107 | + End: 3, | ||
| 108 | + Weight: 2, | ||
| 109 | + }, | ||
| 110 | + { | ||
| 111 | + Start: 1, | ||
| 112 | + End: 2, | ||
| 113 | + Weight: 1, | ||
| 114 | + }, | ||
| 115 | + { | ||
| 116 | + Start: 1, | ||
| 117 | + End: 3, | ||
| 118 | + Weight: 2, | ||
| 119 | + }, | ||
| 120 | + { | ||
| 121 | + Start: 2, | ||
| 122 | + End: 3, | ||
| 123 | + Weight: 3, | ||
| 124 | + }, | ||
| 125 | + }, | ||
| 126 | + cost: 4, | ||
| 127 | + }, | ||
| 128 | + { | ||
| 129 | + n: 2, | ||
| 130 | + graph: []Edge{ | ||
| 131 | + { | ||
| 132 | + Start: 0, | ||
| 133 | + End: 1, | ||
| 134 | + Weight: 4000000, | ||
| 135 | + }, | ||
| 136 | + }, | ||
| 137 | + cost: 4000000, | ||
| 138 | + }, | ||
| 139 | + { | ||
| 140 | + n: 1, | ||
| 141 | + graph: []Edge{ | ||
| 142 | + { | ||
| 143 | + Start: 0, | ||
| 144 | + End: 0, | ||
| 145 | + Weight: 0, | ||
| 146 | + }, | ||
| 147 | + }, | ||
| 148 | + cost: 0, | ||
| 149 | + }, | ||
| 150 | + } | ||
| 151 | + | ||
| 152 | + for i := range testCases { | ||
| 153 | + | ||
| 154 | + t.Run(fmt.Sprintf("Test Case %d", i), func(t *testing.T) { | ||
| 155 | + | ||
| 156 | + _, computed := KruskalMST(testCases[i].n, testCases[i].graph) | ||
| 157 | + if computed != testCases[i].cost { | ||
| 158 | + t.Errorf("Test Case %d, Expected: %d, Computed: %d", i, testCases[i].cost, computed) | ||
| 159 | + } | ||
| 160 | + }) | ||
| 161 | + } | ||
| 162 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments