Documentation
¶
Overview ¶
Package goraph implements graph data structure and algorithms.
Index ¶
- func Kruskal(g Graph) (map[Edge]struct{}, error)
- func MakeDisjointSet(forests *Forests, name string)
- func Prim(g Graph, src ID) (map[Edge]struct{}, error)
- func Tarjan(g Graph) [][]ID
- func Union(forests *Forests, ds1, ds2 *DisjointSet)
- type DisjointSet
- type Edge
- type EdgeSlice
- type Forests
- type Graph
- type ID
- type Node
- type StringID
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Kruskal ¶
Kruskal finds the minimum spanning tree with disjoint-set data structure. (http://en.wikipedia.org/wiki/Kruskal%27s_algorithm)
- Kruskal(G) 1.
- A = ∅ 3.
- for each vertex v in G:
- MakeDisjointSet(v) 6.
- edges = get all edges
- sort edges in ascending order of weight 9.
- for each edge (u, v) in edges:
- if FindSet(u) ≠ FindSet(v):
- A = A ∪ {(u, v)}
- Union(u, v) 14.
- return A
func MakeDisjointSet ¶
MakeDisjointSet creates a DisjointSet.
func Prim ¶
Prim finds the minimum spanning tree with min-heap (priority queue). (http://en.wikipedia.org/wiki/Prim%27s_algorithm)
- Prim(G, source) 1.
- let Q be a priority queue
- distance[source] = 0 4.
- for each vertex v in G: 6.
- if v ≠ source:
- distance[v] = ∞
- prev[v] = undefined 10.
- Q.add_with_priority(v, distance[v]) 12. 13.
- while Q is not empty: 15.
- u = Q.extract_min() 17.
- for each adjacent vertex v of u: 19.
- if v ∈ Q and distance[v] > weight(u, v):
- distance[v] = weight(u, v)
- prev[v] = u
- Q.decrease_priority(v, weight(u, v)) 25. 26.
- return tree from prev
func Tarjan ¶
Tarjan finds the strongly connected components. In the mathematics, a directed graph is "strongly connected" if every vertex is reachable from every other node. Therefore, a graph is strongly connected if there is a path in each direction between each pair of node of a graph. Then a pair of vertices u and v is strongly connected to each other because there is a path in each direction. "Strongly connected components" of an arbitrary graph partition into sub-graphs that are themselves strongly connected. That is, "strongly connected component" of a directed graph is a sub-graph that is strongly connected. Formally, "Strongly connected components" of a graph is a maximal set of vertices C in G.V such that for all u, v ∈ C, there is a path both from u to v, and from v to u. (https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm)
- Tarjan(G): 1.
- globalIndex = 0 // smallest unused index
- let S be a stack
- result = [][] 5.
- for each vertex v in G:
- if v.index is undefined:
- tarjan(G, v, globalIndex, S, result) 9.
- return result 11. 12.
- tarjan(G, v, globalIndex, S, result): 14.
- v.index = globalIndex
- v.lowLink = globalIndex
- globalIndex++
- S.push(v) 19.
- for each child vertex w of v: 21.
- if w.index is undefined:
- recursively tarjan(G, w, globalIndex, S, result)
- v.lowLink = min(v.lowLink, w.lowLink) 25.
- else if w is in S:
- v.lowLink = min(v.lowLink, w.index) 28.
- // if v is the root
- if v.lowLink == v.index: 31.
- // start a new strongly connected component
- component = [] 34.
- while True: 36.
- u = S.pop()
- component.push(u) 39.
- if u == v:
- result.push(component)
- break
func Union ¶
func Union(forests *Forests, ds1, ds2 *DisjointSet)
Union unions two DisjointSet, with ds1's represent.
Types ¶
type DisjointSet ¶
type DisjointSet struct {
// contains filtered or unexported fields
}
DisjointSet implements disjoint set. (https://en.wikipedia.org/wiki/Disjoint-set_data_structure)
func FindSet ¶
func FindSet(forests *Forests, name string) *DisjointSet
FindSet returns the DisjointSet with the represent name.
type Forests ¶
type Forests struct {
// contains filtered or unexported fields
}
Forests is a set of DisjointSet.
type Graph ¶
type Graph interface {
// Init initializes a Graph.
Init()
// GetNodeCount returns the total number of nodes.
GetNodeCount() int
// GetNode finds the Node.
GetNode(id ID) (Node, error)
// GetNodes returns a map from node ID to
// empty struct value. Graph does not allow duplicate
// node ID or name.
GetNodes() map[ID]Node
// AddNode adds a node to a graph, and returns false
// if the node already existed in the graph.
AddNode(nd Node) bool
// DeleteNode deletes a node from a graph.
// It returns true if it got deleted.
// And false if it didn't get deleted.
DeleteNode(id ID) bool
// AddEdge adds an edge from nd1 to nd2 with the weight.
// It returns error if a node does not exist.
AddEdge(id1, id2 ID, weight float64) error
// ReplaceEdge replaces an edge from id1 to id2 with the weight.
ReplaceEdge(id1, id2 ID, weight float64) error
// DeleteEdge deletes an edge from id1 to id2.
DeleteEdge(id1, id2 ID) error
// GetWeight returns the weight from id1 to id2.
GetWeight(id1, id2 ID) (float64, error)
// GetSources returns the map of parent Nodes.
// (Nodes that come towards the argument vertex.)
GetSources(id ID) (map[ID]Node, error)
// GetTargets returns the map of child Nodes.
// (Nodes that go out of the argument vertex.)
GetTargets(id ID) (map[ID]Node, error)
// String describes the Graph.
String() string
}
Graph describes the methods of graph operations. It assumes that the identifier of a Node is unique. And weight values is float64.
func NewGraph ¶
func NewGraph() Graph
NewGraph returns a new graph.
Example ¶
package main
import (
"fmt"
"log"
"os"
"github.com/gyuho/goraph"
)
func main() {
f, err := os.Open("testdata/graph.json")
if err != nil {
log.Fatal(err)
}
defer f.Close()
g, err := goraph.NewGraphFromJSON(f, "graph_00")
if err != nil {
log.Fatal(err)
}
fmt.Println(g.String())
// Output for g.String() but it's unordered because it's map:
// F -- 11.000 -→ D
// F -- 6.000 -→ E
// F -- 6.000 -→ T
// E -- 6.000 -→ F
// E -- 19.000 -→ T
// E -- 18.000 -→ B
// E -- 24.000 -→ C
// E -- 2.000 -→ D
// S -- 100.000 -→ A
// S -- 14.000 -→ B
// S -- 200.000 -→ C
// B -- 14.000 -→ S
// B -- 5.000 -→ A
// B -- 30.000 -→ D
// B -- 18.000 -→ E
// C -- 9.000 -→ S
// C -- 24.000 -→ E
// T -- 16.000 -→ D
// T -- 6.000 -→ F
// T -- 19.000 -→ E
// T -- 44.000 -→ A
// A -- 5.000 -→ B
// A -- 20.000 -→ D
// A -- 44.000 -→ T
// A -- 15.000 -→ S
// D -- 11.000 -→ F
// D -- 16.000 -→ T
// D -- 20.000 -→ A
// D -- 30.000 -→ B
// D -- 2.000 -→ E
}
func NewGraphFromJSON ¶
NewGraphFromJSON returns a new Graph from a JSON file. Here's the sample JSON data:
{
"graph_00": {
"S": {
"A": 100,
"B": 14,
"C": 200
},
"A": {
"S": 15,
"B": 5,
"D": 20,
"T": 44
},
"B": {
"S": 14,
"A": 5,
"D": 30,
"E": 18
},
"C": {
"S": 9,
"E": 24
},
"D": {
"A": 20,
"B": 30,
"E": 2,
"F": 11,
"T": 16
},
"E": {
"B": 18,
"C": 24,
"D": 2,
"F": 6,
"T": 19
},
"F": {
"D": 11,
"E": 6,
"T": 6
},
"T": {
"A": 44,
"D": 16,
"F": 6,
"E": 19
}
},
}
func NewGraphFromYAML ¶
NewGraphFromYAML returns a new Graph from a YAML file. Here's the sample YAML data:
graph_00:
S: A: 100 B: 14 C: 200 A: S: 15 B: 5 D: 20 T: 44 B: S: 14 A: 5 D: 30 E: 18 C: S: 9 E: 24 D: A: 20 B: 30 E: 2 F: 11 T: 16 E: B: 18 C: 24 D: 2 F: 6 T: 19 F: D: 11 E: 6 T: 6 T: A: 44 D: 16 F: 6 E: 19
type ID ¶
type ID interface {
// String returns the string ID.
String() string
}
ID is unique identifier.
func BFS ¶
BFS does breadth-first search, and returns the list of vertices. (https://en.wikipedia.org/wiki/Breadth-first_search)
- BFS(G, v): 1.
- let Q be a queue
- Q.push(v)
- label v as visited 5.
- while Q is not empty: 7.
- u = Q.dequeue() 9.
- for each vertex w adjacent to u: 11.
- if w is not visited yet:
- Q.push(w)
- label w as visited
func BellmanFord ¶
BellmanFord returns the shortest path using Bellman-Ford algorithm This algorithm works with negative weight edges. Time complexity is O(|V||E|). (http://courses.csail.mit.edu/6.006/spring11/lectures/lec15.pdf) It returns error when there is a negative-weight cycle. A negatively-weighted cycle adds up to infinite negative-weight.
- BellmanFord(G, source, target) 1.
- distance[source] = 0 3.
- for each vertex v in G: 5.
- if v ≠ source:
- distance[v] = ∞
- prev[v] = undefined 9. 10.
- for 1 to |V|-1: 12.
- for every edge (u, v): 14.
- alt = distance[u] + weight(u, v)
- if distance[v] > alt:
- distance[v] = alt
- prev[v] = u 19. 20.
- for every edge (u, v): 22.
- alt = distance[u] + weight(u, v)
- if distance[v] > alt:
- there is a negative-weight cycle 26. 27.
- path = []
- u = target
- while prev[u] is defined:
- path.push_front(u)
- u = prev[u] 33.
- return path, prev
func DFS ¶
DFS does depth-first search, and returns the list of vertices. (https://en.wikipedia.org/wiki/Depth-first_search)
- DFS(G, v): 1.
- let S be a stack
- S.push(v) 4.
- while S is not empty: 6.
- u = S.pop() 8.
- if u is not visited yet: 10.
- label u as visited 12.
- for each vertex w adjacent to u: 14.
- if w is not visited yet:
- S.push(w)
func DFSRecursion ¶
DFSRecursion does depth-first search recursively.
- DFS(G, v): 1.
- if v is visited:
- return 4.
- label v as visited 6.
- for each vertex u adjacent to v: 8.
- if u is not visited yet:
- recursive DFS(G, u)
func Dijkstra ¶
Dijkstra returns the shortest path using Dijkstra algorithm with a min-priority queue. This algorithm does not work with negative weight edges. (https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm)
- Dijkstra(G, source, target) 1.
- let Q be a priority queue
- distance[source] = 0 4.
- for each vertex v in G: 6.
- if v ≠ source:
- distance[v] = ∞
- prev[v] = undefined 10.
- Q.add_with_priority(v, distance[v]) 12.
- while Q is not empty: 14.
- u = Q.extract_min()
- if u == target:
- break 18.
- for each child vertex v of u: 20.
- alt = distance[u] + weight(u, v)
- if distance[v] > alt:
- distance[v] = alt
- prev[v] = u
- Q.decrease_priority(v, alt) 26.
- reheapify(Q) 28. 29.
- path = []
- u = target
- while prev[u] is defined:
- path.push_front(u)
- u = prev[u] 35.
- return path, prev
func TopologicalSort ¶
TopologicalSort does topological sort(ordering) with DFS. It returns true if the graph is a DAG (no cycle, with a topological sort). False if the graph is not a DAG (cycle, with no topological sort).
- TopologicalSort(G) 1.
- L = Empty list that will contain the sorted nodes
- isDAG = true 4.
- for each vertex v in G: 6.
- if v.color == "white": 8.
- topologicalSortVisit(v, L, isDAG) 10. 11. 12. 13.
- topologicalSortVisit(v, L, isDAG) 15.
- if v.color == "gray":
- isDAG = false
- return 19.
- if v.color == "white": 21.
- v.color = "gray": 23.
- for each child vertex w of v:
- topologicalSortVisit(w, L, isDAG) 26.
- v.color = "black"
- L.push_front(v)
