293dd2e848
* Add vendor dir so builds dont require dep * Pin specific version go-eth version
127 lines
3.2 KiB
Go
Executable File
127 lines
3.2 KiB
Go
Executable File
// CookieJar - A contestant's algorithm toolbox
|
|
// Copyright 2013 Peter Szilagyi. All rights reserved.
|
|
//
|
|
// CookieJar is dual licensed: you can redistribute it and/or modify it under
|
|
// the terms of the GNU General Public License as published by the Free Software
|
|
// Foundation, either version 3 of the License, or (at your option) any later
|
|
// version.
|
|
//
|
|
// The toolbox is distributed in the hope that it will be useful, but WITHOUT
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
// more details.
|
|
//
|
|
// 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 dfs implements the depth-first-search algorithm for the graphs.
|
|
//
|
|
// The DFS 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 dfs
|
|
|
|
import (
|
|
"gopkg.in/karalabe/cookiejar.v2/collections/stack"
|
|
"gopkg.in/karalabe/cookiejar.v2/graph"
|
|
)
|
|
|
|
// Depth-first-search algorithm data structure.
|
|
type Dfs struct {
|
|
graph *graph.Graph
|
|
source int
|
|
|
|
visited []bool
|
|
parents []int
|
|
order []int
|
|
|
|
pending *stack.Stack
|
|
builder *stack.Stack
|
|
}
|
|
|
|
// Creates a new random-order dfs structure.
|
|
func New(g *graph.Graph, src int) *Dfs {
|
|
d := new(Dfs)
|
|
|
|
d.graph = g
|
|
d.source = src
|
|
|
|
d.visited = make([]bool, g.Vertices())
|
|
d.parents = make([]int, g.Vertices())
|
|
d.order = make([]int, 0, g.Vertices())
|
|
|
|
d.pending = stack.New()
|
|
d.pending.Push(src)
|
|
d.builder = stack.New()
|
|
|
|
return d
|
|
}
|
|
|
|
// Generates the path from the source node to the destination.
|
|
func (d *Dfs) Path(dst int) []int {
|
|
// If not found yet, but processing's not done, search
|
|
if !d.visited[dst] && !d.pending.Empty() {
|
|
d.search(dst)
|
|
}
|
|
// If done but still not found return a nil slice
|
|
if !d.visited[dst] {
|
|
return nil
|
|
}
|
|
// Generate the path and return
|
|
for dst != d.source {
|
|
d.builder.Push(dst)
|
|
dst = d.parents[dst]
|
|
}
|
|
d.builder.Push(dst)
|
|
|
|
path := make([]int, d.builder.Size())
|
|
for i := 0; i < len(path); i++ {
|
|
path[i] = d.builder.Pop().(int)
|
|
}
|
|
return path
|
|
}
|
|
|
|
// Checks whether a given vertex is reachable from the source.
|
|
func (d *Dfs) 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 *Dfs) Order() []int {
|
|
// Force dfs termination
|
|
if !d.pending.Empty() {
|
|
d.search(-1)
|
|
}
|
|
return d.order
|
|
}
|
|
|
|
// Continues the DFS search from the last yield point, looking for dst.
|
|
func (d *Dfs) search(dst int) {
|
|
for !d.pending.Empty() {
|
|
// Fetch the next node, and visit if new
|
|
src := d.pending.Pop().(int)
|
|
if !d.visited[src] {
|
|
d.visited[src] = true
|
|
d.order = append(d.order, src)
|
|
|
|
d.graph.Do(src, func(peer interface{}) {
|
|
if p := peer.(int); !d.visited[p] {
|
|
d.parents[p] = src
|
|
d.pending.Push(p)
|
|
}
|
|
})
|
|
}
|
|
// If we found the destination, yield
|
|
if dst == src {
|
|
return
|
|
}
|
|
}
|
|
}
|