Skip to content

Commit a7079a9

Browse files
committed
Update algorithms
1 parent f5c5a37 commit a7079a9

File tree

4 files changed

+187
-14
lines changed

4 files changed

+187
-14
lines changed

algo/graphs/topsort.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package graphs
2+
3+
import (
4+
"container/heap"
5+
"github.com/code-shoily/aocgo/algo"
6+
)
7+
8+
// TopologicalSort performs a topological sort based on FIFO and returns the sorted order (nil if cycle) and cycle
9+
func TopologicalSort(g *Graph) (sorted []string, cycle bool) {
10+
var count int
11+
inDegrees := make(map[string]int)
12+
queue := algo.NewQueue()
13+
14+
for _, vertex := range g.Vertices() {
15+
if inDegree := len(vertex.incoming); inDegree == 0 {
16+
queue.Enqueue(vertex.ID())
17+
} else {
18+
inDegrees[vertex.ID()] += inDegree
19+
}
20+
}
21+
22+
for !queue.IsEmpty() {
23+
if vertexAny, empty := queue.Dequeue(); !empty {
24+
vertexID := vertexAny.(string)
25+
sorted = append(sorted, vertexID)
26+
27+
for outgoing, _ := range g.Vertices()[vertexID].outgoing {
28+
outgoingID := outgoing.ID()
29+
inDegrees[outgoingID]--
30+
31+
if inDegrees[outgoingID] == 0 {
32+
queue.Enqueue(outgoingID)
33+
}
34+
}
35+
}
36+
count++
37+
}
38+
39+
if count != len(g.Vertices()) {
40+
return nil, true
41+
}
42+
43+
return sorted, cycle
44+
}
45+
46+
func lexicographicalSorter(g *Graph, h heap.Interface) (sorted []string, cycle bool) {
47+
var count int
48+
inDegrees := make(map[string]int)
49+
heap.Init(h)
50+
51+
for _, vertex := range g.Vertices() {
52+
if inDegree := len(vertex.incoming); inDegree == 0 {
53+
heap.Push(h, vertex.ID())
54+
} else {
55+
inDegrees[vertex.ID()] += inDegree
56+
}
57+
}
58+
59+
for h.Len() != 0 {
60+
vertexAny := heap.Pop(h)
61+
vertexID := vertexAny.(string)
62+
sorted = append(sorted, vertexID)
63+
64+
for outgoing, _ := range g.Vertices()[vertexID].outgoing {
65+
outgoingID := outgoing.ID()
66+
inDegrees[outgoingID]--
67+
68+
if inDegrees[outgoingID] == 0 {
69+
heap.Push(h, outgoingID)
70+
}
71+
}
72+
73+
count++
74+
}
75+
76+
if count != len(g.Vertices()) {
77+
return nil, true
78+
}
79+
80+
return sorted, cycle
81+
}
82+
83+
// LexicographicalTopologicalSort returns sort in ascending order of keys unless a cycle is detected.
84+
func LexicographicalTopologicalSort(g *Graph) (sorted []string, cycle bool) {
85+
return lexicographicalSorter(g, &algo.Heap[string]{})
86+
}
87+
88+
// LexicographicalReverseTopologicalSort returns sort in descending order of keys unless a cycle is detected.
89+
func LexicographicalReverseTopologicalSort(g *Graph) (sorted []string, cycle bool) {
90+
return lexicographicalSorter(g, &algo.MaxHeap[string]{})
91+
}

algo/graphs/topsort_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package graphs
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func MakeGraph() *Graph {
9+
g := NewGraph(true)
10+
for _, vertexID := range []string{"0", "1", "2", "3", "4", "5"} {
11+
g.AddVertex(NewSimpleVertex(vertexID))
12+
}
13+
g.AddEdge("2", "3", 1)
14+
g.AddEdge("3", "1", 1)
15+
g.AddEdge("4", "0", 1)
16+
g.AddEdge("4", "1", 1)
17+
g.AddEdge("5", "2", 1)
18+
g.AddEdge("5", "0", 1)
19+
20+
return g
21+
}
22+
23+
func MakeGraphWithCycle() *Graph {
24+
g := NewGraph(true)
25+
for _, vertexID := range []string{"0", "1", "2", "3", "4", "5"} {
26+
g.AddVertex(NewSimpleVertex(vertexID))
27+
}
28+
g.AddEdge("2", "3", 1)
29+
g.AddEdge("3", "1", 1)
30+
g.AddEdge("4", "0", 1)
31+
g.AddEdge("4", "1", 1)
32+
g.AddEdge("5", "2", 1)
33+
g.AddEdge("5", "0", 1)
34+
g.AddEdge("3", "2", 1)
35+
36+
return g
37+
}
38+
39+
func TestTopologicalSort(t *testing.T) {
40+
graph := MakeGraph()
41+
expect := []string{"4", "5", "2", "0", "3", "1"}
42+
if sorted, cycle := TopologicalSort(graph); !reflect.DeepEqual(sorted, expect) || cycle {
43+
t.Errorf("Fail - expected %v/%v but got %v/%v", expect, false, sorted, cycle)
44+
}
45+
}
46+
47+
func TestTopologicalSortCycled(t *testing.T) {
48+
graph := MakeGraphWithCycle()
49+
if _, cycle := TopologicalSort(graph); !cycle {
50+
t.Errorf("Fail - expected %v but got %v", true, cycle)
51+
}
52+
}
53+
54+
func TestLexicographicalTopologicalSort(t *testing.T) {
55+
graph := MakeGraph()
56+
expect := []string{"4", "5", "0", "2", "3", "1"}
57+
if sorted, cycle := LexicographicalTopologicalSort(graph); !reflect.DeepEqual(sorted, expect) || cycle {
58+
t.Errorf("Fail - expected %v/%v but got %v/%v", expect, false, sorted, cycle)
59+
}
60+
}
61+
62+
func TestLexicographicalTopologicalSortCycled(t *testing.T) {
63+
graph := MakeGraphWithCycle()
64+
if _, cycle := LexicographicalTopologicalSort(graph); !cycle {
65+
t.Errorf("Fail - expected %v but got %v", true, cycle)
66+
}
67+
}
68+
69+
func TestLexicographicalReverseTopologicalSort(t *testing.T) {
70+
graph := MakeGraph()
71+
expect := []string{"5", "4", "2", "3", "1", "0"}
72+
if sorted, cycle := LexicographicalReverseTopologicalSort(graph); !reflect.DeepEqual(sorted, expect) || cycle {
73+
t.Errorf("Fail - expected %v/%v but got %v/%v", expect, false, sorted, cycle)
74+
}
75+
}
76+
77+
func TestLexicographicalReverseTopologicalSortCycled(t *testing.T) {
78+
graph := MakeGraphWithCycle()
79+
if _, cycle := LexicographicalReverseTopologicalSort(graph); !cycle {
80+
t.Errorf("Fail - expected %v but got %v", true, cycle)
81+
}
82+
}

algo/queue.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,39 @@ package algo
33
import "container/list"
44

55
// NewQueue creates a new Queue
6-
func NewQueue[T any]() *Queue[T] {
7-
return &Queue[T]{list.New()}
6+
func NewQueue() *Queue {
7+
return &Queue{list.New()}
88
}
99

10-
type Queue[T any] struct {
10+
type Queue struct {
1111
*list.List
1212
}
1313

1414
// Enqueue insers a new element on the queue
15-
func (q *Queue[T]) Enqueue(elem T) {
15+
func (q *Queue) Enqueue(elem any) {
1616
q.PushBack(elem)
1717
}
1818

1919
// Dequeue removes and returns the oldest element from the queue
20-
func (q *Queue[T]) Dequeue() (elem T, empty bool) {
20+
func (q *Queue) Dequeue() (elem any, empty bool) {
2121
if q.IsEmpty() {
2222
return elem, true
2323
}
2424
front := q.Front()
2525
q.Remove(front)
2626

27-
return front.Value.(T), false
27+
return front.Value, false
2828
}
2929

3030
// Peek shows the oldest element in the queue without removing anything
31-
func (q *Queue[T]) Peek() (elem T, empty bool) {
31+
func (q *Queue) Peek() (elem any, empty bool) {
3232
if q.IsEmpty() {
3333
return elem, true
3434
}
35-
return q.Front().Value.(T), false
35+
return q.Front().Value, false
3636
}
3737

3838
// IsEmpty checks if the queue is empty
39-
func (q *Queue[T]) IsEmpty() bool {
39+
func (q *Queue) IsEmpty() bool {
4040
return q.Len() == 0
4141
}

algo/queue_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package algo
33
import "testing"
44

55
func TestQueue_IsEmpty(t *testing.T) {
6-
queue := NewQueue[string]()
6+
queue := NewQueue()
77

88
if empty := queue.IsEmpty(); !empty {
99
t.Errorf("Fail - expected the queue to be empty")
@@ -23,7 +23,7 @@ func TestQueue_IsEmpty(t *testing.T) {
2323
}
2424

2525
func TestQueue_EnqueueDeque(t *testing.T) {
26-
queue := NewQueue[int]()
26+
queue := NewQueue()
2727
queue.Enqueue(1)
2828
queue.Enqueue(2)
2929
queue.Enqueue(3)
@@ -37,13 +37,13 @@ func TestQueue_EnqueueDeque(t *testing.T) {
3737
if e, notEmpty := queue.Dequeue(); e != 3 || notEmpty {
3838
t.Errorf("Fail - %v/%v expected, got %v/%v", 1, false, e, notEmpty)
3939
}
40-
if e, empty := queue.Dequeue(); e != 0 || !empty {
40+
if e, empty := queue.Dequeue(); e != nil || !empty {
4141
t.Errorf("Fail - %v/%v expected, got %v/%v", 1, true, e, empty)
4242
}
4343
}
4444

4545
func TestQueue_Peek(t *testing.T) {
46-
queue := NewQueue[int]()
46+
queue := NewQueue()
4747
queue.Enqueue(1)
4848
if e, notEmpty := queue.Peek(); e != 1 || notEmpty {
4949
t.Errorf("Fail - %v/%v expected, got %v/%v", 1, false, e, notEmpty)
@@ -63,7 +63,7 @@ func TestQueue_Peek(t *testing.T) {
6363
queue.Dequeue()
6464
queue.Dequeue()
6565

66-
if e, empty := queue.Peek(); e != 0 || !empty {
66+
if e, empty := queue.Peek(); e != nil || !empty {
6767
t.Errorf("Fail - %v/%v expected, got %v/%v", 1, true, e, empty)
6868
}
6969
}

0 commit comments

Comments
 (0)