diff --git a/collection.go b/collection.go index d078f064..8340cb6a 100644 --- a/collection.go +++ b/collection.go @@ -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) + } +} diff --git a/collection_test.go b/collection_test.go index f591dce6..5a1044b7 100644 --- a/collection_test.go +++ b/collection_test.go @@ -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) +} diff --git a/paths.go b/paths.go index 7ff2fbac..73fc87a5 100644 --- a/paths.go +++ b/paths.go @@ -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 +} diff --git a/paths_test.go b/paths_test.go index a7771c36..ee1ee149 100644 --- a/paths_test.go +++ b/paths_test.go @@ -1,6 +1,8 @@ package graph import ( + "reflect" + "sort" "testing" ) @@ -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) + } + }) + } +}