Skip to content

Commit

Permalink
feat(dominikbraun#82): List all path between vertices
Browse files Browse the repository at this point in the history
  • Loading branch information
tzq0301 committed Jun 13, 2023
1 parent c6cb265 commit 5b359d1
Show file tree
Hide file tree
Showing 4 changed files with 355 additions and 0 deletions.
53 changes: 53 additions & 0 deletions collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,56 @@ func (m *minHeap[T]) Pop() interface{} {

return item
}

type stack[T any] interface {
push(T)
pop() (T, error)
top() (T, error)
isEmpty() bool
// forEach iterate the stack from bottom to top
forEach(func(T))
}

func newStack[T any]() stack[T] {
return &stackImpl[T]{
elements: make([]T, 0),
}
}

type stackImpl[T any] struct {
elements []T
}

func (s *stackImpl[T]) push(t T) {
s.elements = append(s.elements, t)
}

func (s *stackImpl[T]) pop() (T, error) {
e, err := s.top()
if err != nil {
var defaultValue T
return defaultValue, err
}

s.elements = s.elements[:len(s.elements)-1]
return e, nil
}

func (s *stackImpl[T]) top() (T, error) {
if s.isEmpty() {
var defaultValue T
return defaultValue, errors.New("no element in stack")
}

return s.elements[len(s.elements)-1], nil
}

func (s *stackImpl[T]) isEmpty() bool {
return len(s.elements) == 0
}

func (s *stackImpl[T]) forEach(f func(T)) {
for _, e := range s.elements {
f(e)
}
}
28 changes: 28 additions & 0 deletions collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,31 @@ func TestPriorityQueue_Len(t *testing.T) {
}
}
}

func TestStack(t *testing.T) {
s := newStack[int]()

assert := func(expression bool) {
if !expression {
t.Fatal()
}
}

assert(s.isEmpty())

s.push(1)
assert(!s.isEmpty())
e, _ := s.top()
assert(e == 1)
e, _ = s.pop()
assert(e == 1)
assert(s.isEmpty())

s.push(1)
s.push(2)
s.push(3)
e, _ = s.pop()
assert(e == 3)
e, _ = s.top()
assert(e == 2)
}
101 changes: 101 additions & 0 deletions paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,104 @@ func findSCC[K comparable](vertexHash K, state *sccState[K]) {
state.components = append(state.components, component)
}
}

// AllPathsBetweenTwoVertices list all paths from start to end.
func AllPathsBetweenTwoVertices[K comparable, T any](g Graph[K, T], start, end K) ([][]K, error) {
adjacencyMap, err := g.AdjacencyMap()
if err != nil {
return nil, err
}

mainStack, viceStack := newStack[K](), newStack[stack[K]]()

checkEmpty := func() error {
if mainStack.isEmpty() || viceStack.isEmpty() {
return errors.New("empty stack")
}
return nil
}

buildLayer := func(element K) {
mainStack.push(element)

newElements := newStack[K]()
for e := range adjacencyMap[element] {
var contains bool
mainStack.forEach(func(k K) {
if e == k {
contains = true
}
})
if contains {
continue
}
newElements.push(e)
}
viceStack.push(newElements)
}

buildStack := func() error {
if err := checkEmpty(); err != nil {
return errors.New("empty stack")
}

elements, _ := viceStack.top()
for !elements.isEmpty() {
element, _ := elements.pop()
buildLayer(element)
elements, _ = viceStack.top()
}

return nil
}

removeLayer := func() error {
if err := checkEmpty(); err != nil {
return errors.New("empty stack")
}

e, _ := viceStack.top()
if !e.isEmpty() {
return errors.New("the top element of vice-stack is not empty")
}

_, _ = mainStack.pop()
_, _ = viceStack.pop()

return nil
}

// init the first layer

buildLayer(start)

// loop

allPaths := make([][]K, 0)

for !mainStack.isEmpty() {
v, _ := mainStack.top()
adjs, _ := viceStack.top()

if adjs.isEmpty() {
if v == end {
path := make([]K, 0)
mainStack.forEach(func(k K) {
path = append(path, k)
})
allPaths = append(allPaths, path)
}

err = removeLayer()
if err != nil {
return nil, err
}
} else {
if err = buildStack(); err != nil {
return nil, err
}
}
}

return allPaths, nil
}
173 changes: 173 additions & 0 deletions paths_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package graph

import (
"reflect"
"sort"
"testing"
)

Expand Down Expand Up @@ -496,3 +498,174 @@ func TestUndirectedStronglyConnectedComponents(t *testing.T) {
}
}
}

func TestAllPathBetweenTwoVertices(t *testing.T) {
type args[K comparable, T any] struct {
g Graph[K, T]
start K
end K
}
type testCase[K comparable, T any] struct {
name string
args args[K, T]
want [][]K
wantErr bool
}
tests := []testCase[int, int]{
{
name: "directed",
args: args[int, int]{
g: func() Graph[int, int] {
g := New(IntHash, Directed())
for i := 0; i <= 8; i++ {
_ = g.AddVertex(i)
}
_ = g.AddEdge(0, 2)
_ = g.AddEdge(1, 0)
_ = g.AddEdge(1, 4)
_ = g.AddEdge(2, 6)
_ = g.AddEdge(3, 1)
_ = g.AddEdge(3, 7)
_ = g.AddEdge(4, 5)
_ = g.AddEdge(5, 2)
_ = g.AddEdge(5, 6)
_ = g.AddEdge(6, 8)
_ = g.AddEdge(7, 4)
return g
}(),
start: 3,
end: 6,
},
want: func() [][]int {
allPath := make([][]int, 0)
addPath := func(elements ...int) {
allPath = append(allPath, elements)
}
addPath(3, 1, 0, 2, 6)
addPath(3, 1, 4, 5, 6)
addPath(3, 1, 4, 5, 2, 6)
addPath(3, 7, 4, 5, 2, 6)
addPath(3, 7, 4, 5, 6)
return allPath
}(),
wantErr: false,
},
{
name: "undirected",
args: args[int, int]{
g: func() Graph[int, int] {
g := New(IntHash)
for i := 0; i <= 8; i++ {
_ = g.AddVertex(i)
}
_ = g.AddEdge(0, 1)
_ = g.AddEdge(0, 2)
_ = g.AddEdge(1, 3)
_ = g.AddEdge(1, 4)
_ = g.AddEdge(2, 5)
_ = g.AddEdge(2, 6)
_ = g.AddEdge(3, 7)
_ = g.AddEdge(4, 5)
_ = g.AddEdge(4, 7)
_ = g.AddEdge(5, 6)
_ = g.AddEdge(6, 8)
return g
}(),
start: 3,
end: 6,
},
want: func() [][]int {
allPath := make([][]int, 0)
addPath := func(elements ...int) {
allPath = append(allPath, elements)
}
addPath(3, 1, 0, 2, 6)
addPath(3, 1, 0, 2, 5, 6)
addPath(3, 1, 4, 5, 6)
addPath(3, 1, 4, 5, 2, 6)
addPath(3, 7, 4, 5, 2, 6)
addPath(3, 7, 4, 5, 6)
addPath(3, 7, 4, 1, 0, 2, 6)
addPath(3, 7, 4, 1, 0, 2, 5, 6)
return allPath
}(),
wantErr: false,
},
{
name: "undirected (complex)",
args: args[int, int]{
g: func() Graph[int, int] {
g := New(IntHash)
for i := 0; i <= 9; i++ {
_ = g.AddVertex(i)
}
_ = g.AddEdge(0, 1)
_ = g.AddEdge(0, 2)
_ = g.AddEdge(0, 3)
_ = g.AddEdge(2, 3)
_ = g.AddEdge(2, 6)
_ = g.AddEdge(3, 4)
_ = g.AddEdge(4, 8)
_ = g.AddEdge(5, 6)
_ = g.AddEdge(5, 8)
_ = g.AddEdge(5, 9)
_ = g.AddEdge(6, 7)
_ = g.AddEdge(7, 9)
_ = g.AddEdge(8, 9)
return g
}(),
start: 0,
end: 9,
},
want: func() [][]int {
allPath := make([][]int, 0)
addPath := func(elements ...int) {
allPath = append(allPath, elements)
}
addPath(0, 2, 3, 4, 8, 5, 6, 7, 9)
addPath(0, 2, 3, 4, 8, 5, 9)
addPath(0, 2, 3, 4, 8, 9)
addPath(0, 2, 6, 5, 8, 9)
addPath(0, 2, 6, 5, 9)
addPath(0, 2, 6, 7, 9)
addPath(0, 3, 2, 6, 5, 8, 9)
addPath(0, 3, 2, 6, 5, 9)
addPath(0, 3, 2, 6, 7, 9)
addPath(0, 3, 4, 8, 5, 6, 7, 9)
addPath(0, 3, 4, 8, 5, 9)
addPath(0, 3, 4, 8, 9)
return allPath
}(),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := AllPathsBetweenTwoVertices(tt.args.g, tt.args.start, tt.args.end)
if (err != nil) != tt.wantErr {
t.Errorf("AllPathsBetweenTwoVertices() error = %v, wantErr %v", err, tt.wantErr)
return
}

toStr := func(s []int) string {
var num string
for _, n := range s {
num = num + string(rune(n))
}
return num
}

sort.Slice(got, func(i, j int) bool {
return toStr(got[i]) < toStr(got[j])
})

sort.Slice(tt.want, func(i, j int) bool {
return toStr(tt.want[i]) < toStr(tt.want[j])
})

if !reflect.DeepEqual(got, tt.want) {
t.Errorf("AllPathsBetweenTwoVertices() got = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit 5b359d1

Please sign in to comment.