293dd2e848
* Add vendor dir so builds dont require dep * Pin specific version go-eth version
121 lines
2.9 KiB
Go
Executable File
121 lines
2.9 KiB
Go
Executable File
// CookieJar - A contestant's algorithm toolbox
|
|
// Copyright 2013 Peter Szilagyi. All rights reserved.
|
|
//
|
|
// CookieJar is dual licensed: use of this source code is governed by a BSD
|
|
// license that can be found in the LICENSE file. Alternatively, the CookieJar
|
|
// toolbox may be used in accordance with the terms and conditions contained
|
|
// in a signed written agreement between you and the author(s).
|
|
|
|
// Package bfs implements the breadth-first-search algorithm for the graphs.
|
|
//
|
|
// The BFS is implemented using on demand calculations, meaning that only that
|
|
// part of the search space will be expanded as requested, iteratively expanding
|
|
// it if needed.
|
|
//
|
|
// Neighbor traversal order currently is random due to the graph implementation.
|
|
// Specific order may be added in the future.
|
|
package bfs
|
|
|
|
import (
|
|
"gopkg.in/karalabe/cookiejar.v2/collections/queue"
|
|
"gopkg.in/karalabe/cookiejar.v2/collections/stack"
|
|
"gopkg.in/karalabe/cookiejar.v2/graph"
|
|
)
|
|
|
|
// Breadth-first-search algorithm data structure.
|
|
type Bfs struct {
|
|
graph *graph.Graph
|
|
source int
|
|
|
|
visited []bool
|
|
parents []int
|
|
order []int
|
|
paths map[int][]int
|
|
|
|
pending *queue.Queue
|
|
builder *stack.Stack
|
|
}
|
|
|
|
// Creates a new random-order bfs structure.
|
|
func New(g *graph.Graph, src int) *Bfs {
|
|
d := new(Bfs)
|
|
|
|
d.graph = g
|
|
d.source = src
|
|
|
|
d.visited = make([]bool, g.Vertices())
|
|
d.visited[src] = true
|
|
d.parents = make([]int, g.Vertices())
|
|
d.order = make([]int, 1, g.Vertices())
|
|
d.order[0] = src
|
|
d.paths = make(map[int][]int)
|
|
|
|
d.pending = queue.New()
|
|
d.pending.Push(src)
|
|
d.builder = stack.New()
|
|
|
|
return d
|
|
}
|
|
|
|
// Generates the path from the source node to the destination.
|
|
func (d *Bfs) Path(dst int) []int {
|
|
// Return nil if not reachable
|
|
if !d.Reachable(dst) {
|
|
return nil
|
|
}
|
|
// If reachable, but path not yet generated, create and cache
|
|
if cached, ok := d.paths[dst]; !ok {
|
|
for cur := dst; cur != d.source; {
|
|
d.builder.Push(cur)
|
|
cur = d.parents[cur]
|
|
}
|
|
d.builder.Push(d.source)
|
|
|
|
path := make([]int, d.builder.Size())
|
|
for i := 0; i < len(path); i++ {
|
|
path[i] = d.builder.Pop().(int)
|
|
}
|
|
d.paths[dst] = path
|
|
return path
|
|
} else {
|
|
return cached
|
|
}
|
|
}
|
|
|
|
// Checks whether a given vertex is reachable from the source.
|
|
func (d *Bfs) Reachable(dst int) bool {
|
|
if !d.visited[dst] && !d.pending.Empty() {
|
|
d.search(dst)
|
|
}
|
|
return d.visited[dst]
|
|
}
|
|
|
|
// Generates the full order in which nodes were traversed.
|
|
func (d *Bfs) Order() []int {
|
|
// Force bfs termination
|
|
if !d.pending.Empty() {
|
|
d.search(-1)
|
|
}
|
|
return d.order
|
|
}
|
|
|
|
// Continues the bfs search from the last yield point, looking for dst.
|
|
func (d *Bfs) search(dst int) {
|
|
for !d.pending.Empty() {
|
|
// Fetch the next node, and visit if new
|
|
src := d.pending.Pop().(int)
|
|
d.graph.Do(src, func(peer interface{}) {
|
|
if p := peer.(int); !d.visited[p] {
|
|
d.visited[p] = true
|
|
d.order = append(d.order, p)
|
|
d.parents[p] = src
|
|
d.pending.Push(p)
|
|
}
|
|
})
|
|
// If we found the destination, yield
|
|
if dst == src {
|
|
return
|
|
}
|
|
}
|
|
}
|