diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a78979..99022c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.23.0] - 2023-07-05 + +**Are you using graph? [Check out the graph user survey](https://forms.gle/MLKUZKMeCRxTfj4v9)** + +### Added +* Added the `AllPathsBetween` function for computing all paths between two vertices. + ## [0.22.3] - 2023-06-14 ### Changed diff --git a/README.md b/README.md index 3145f74..de85472 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ A library for creating generic graph data structures and modifying, analyzing, and visualizing them. +**Are you using graph? [Check out the graph user survey.](https://forms.gle/MLKUZKMeCRxTfj4v9)** + # Features * Generic vertices of any type, such as `int` or `City`. @@ -398,3 +400,5 @@ To implement the `Store` interface appropriately, take a look at the [documentat # Documentation The full documentation is available at [pkg.go.dev](https://pkg.go.dev/github.com/dominikbraun/graph). + +**Are you using graph? [Check out the graph user survey.](https://forms.gle/MLKUZKMeCRxTfj4v9)** diff --git a/paths.go b/paths.go index 31cbb98..0bb3c68 100644 --- a/paths.go +++ b/paths.go @@ -233,14 +233,22 @@ func findSCC[K comparable](vertexHash K, state *sccState[K]) { } } -// AllPathsBetween list all paths from start to end. +// AllPathsBetween computes and returns all paths between two given vertices. A +// path is represented as a slice of vertex hashes. The returned slice contains +// these paths. +// +// AllPathsBetween utilizes a non-recursive, stack-based implementation. It has +// an estimated runtime complexity of O(n^2) where n is the number of vertices. func AllPathsBetween[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]]() + // The algorithm used relies on stacks instead of recursion. It is described + // here: https://boycgit.github.io/all-paths-between-two-vertex/ + mainStack := newStack[K]() + viceStack := newStack[stack[K]]() checkEmpty := func() error { if mainStack.isEmpty() || viceStack.isEmpty() { @@ -270,10 +278,11 @@ func AllPathsBetween[K comparable, T any](g Graph[K, T], start, end K) ([][]K, e buildStack := func() error { if err = checkEmpty(); err != nil { - return errors.New("empty stack") + return fmt.Errorf("unable to build stack: %w", err) } elements, _ := viceStack.top() + for !elements.isEmpty() { element, _ := elements.pop() buildLayer(element) @@ -285,11 +294,10 @@ func AllPathsBetween[K comparable, T any](g Graph[K, T], start, end K) ([][]K, e removeLayer := func() error { if err = checkEmpty(); err != nil { - return errors.New("empty stack") + return fmt.Errorf("unable to remove layer: %w", err) } - e, _ := viceStack.top() - if !e.isEmpty() { + if e, _ := viceStack.top(); !e.isEmpty() { return errors.New("the top element of vice-stack is not empty") } @@ -299,12 +307,8 @@ func AllPathsBetween[K comparable, T any](g Graph[K, T], start, end K) ([][]K, e return nil } - // init the first layer - buildLayer(start) - // loop - allPaths := make([][]K, 0) for !mainStack.isEmpty() {