FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

feat: kruskal's algorithm using DSU (#374) · gitgitcode/Go@af4ca77 · GitHub

/ Go Public
forked from TheAlgorithms/Go

Commit af4ca77

Browse files
authored
feat: kruskal's algorithm using DSU (TheAlgorithms#374)
1 parent ace84ab commit af4ca77

2 files changed

Lines changed: 275 additions & 0 deletions

File tree

‎graph/kruskal.go‎

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff 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+
}

‎graph/kruskal_test.go‎

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff 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+
}

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL