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 }
Output:
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)