123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- package toposort
- import (
- "bytes"
- "fmt"
- "sort"
- "strings"
- )
- // Reworked from https://github.com/philopon/go-toposort
- // Graph represents a directed acyclic graph.
- type Graph struct {
- // Outgoing connections for every node.
- outputs map[string]map[string]int
- // How many parents each node has.
- inputs map[string]int
- }
- // NewGraph initializes a new Graph.
- func NewGraph() *Graph {
- return &Graph{
- inputs: map[string]int{},
- outputs: map[string]map[string]int{},
- }
- }
- // Copy clones the graph and returns the independent copy.
- func (g *Graph) Copy() *Graph {
- clone := NewGraph()
- for k, v := range g.inputs {
- clone.inputs[k] = v
- }
- for k1, v1 := range g.outputs {
- m := map[string]int{}
- clone.outputs[k1] = m
- for k2, v2 := range v1 {
- m[k2] = v2
- }
- }
- return clone
- }
- // AddNode inserts a new node into the graph.
- func (g *Graph) AddNode(name string) bool {
- if _, exists := g.outputs[name]; exists {
- return false
- }
- g.outputs[name] = make(map[string]int)
- g.inputs[name] = 0
- return true
- }
- // AddNodes inserts multiple nodes into the graph at once.
- func (g *Graph) AddNodes(names ...string) bool {
- for _, name := range names {
- if ok := g.AddNode(name); !ok {
- return false
- }
- }
- return true
- }
- // AddEdge inserts the link from "from" node to "to" node.
- func (g *Graph) AddEdge(from, to string) int {
- m, ok := g.outputs[from]
- if !ok {
- return 0
- }
- m[to] = len(m) + 1
- ni := g.inputs[to] + 1
- g.inputs[to] = ni
- return ni
- }
- // ReindexNode updates the internal representation of the node after edge removals.
- func (g *Graph) ReindexNode(node string) {
- children, ok := g.outputs[node]
- if !ok {
- return
- }
- keys := []string{}
- for key := range children {
- keys = append(keys, key)
- }
- sort.Strings(keys)
- for i, key := range keys {
- children[key] = i + 1
- }
- }
- func (g *Graph) unsafeRemoveEdge(from, to string) {
- delete(g.outputs[from], to)
- g.inputs[to]--
- }
- // RemoveEdge deletes the link from "from" node to "to" node.
- // Call ReindexNode(from) after you finish modifying the edges.
- func (g *Graph) RemoveEdge(from, to string) bool {
- if _, ok := g.outputs[from]; !ok {
- return false
- }
- g.unsafeRemoveEdge(from, to)
- return true
- }
- // Toposort sorts the nodes in the graph in topological order.
- func (g *Graph) Toposort() ([]string, bool) {
- L := make([]string, 0, len(g.outputs))
- S := make([]string, 0, len(g.outputs))
- for n := range g.outputs {
- if g.inputs[n] == 0 {
- S = append(S, n)
- }
- }
- sort.Strings(S)
- for len(S) > 0 {
- var n string
- n, S = S[0], S[1:]
- L = append(L, n)
- ms := make([]string, len(g.outputs[n]))
- for m, i := range g.outputs[n] {
- ms[i-1] = m
- }
- for _, m := range ms {
- g.unsafeRemoveEdge(n, m)
- if g.inputs[m] == 0 {
- S = append(S, m)
- }
- }
- }
- N := 0
- for _, v := range g.inputs {
- N += v
- }
- if N > 0 {
- return L, false
- }
- return L, true
- }
- // BreadthSort sorts the nodes in the graph in BFS order.
- func (g *Graph) BreadthSort() []string {
- L := make([]string, 0, len(g.outputs))
- S := make([]string, 0, len(g.outputs))
- for n := range g.outputs {
- if g.inputs[n] == 0 {
- S = append(S, n)
- }
- }
- visited := map[string]bool{}
- for len(S) > 0 {
- node := S[0]
- S = S[1:]
- if _, exists := visited[node]; !exists {
- L = append(L, node)
- visited[node] = true
- for child := range g.outputs[node] {
- S = append(S, child)
- }
- }
- }
- return L
- }
- // FindCycle returns the cycle in the graph which contains "seed" node.
- func (g *Graph) FindCycle(seed string) []string {
- type edge struct {
- node string
- parent string
- }
- S := make([]edge, 0, len(g.outputs))
- S = append(S, edge{seed, ""})
- visited := map[string]string{}
- for len(S) > 0 {
- e := S[0]
- S = S[1:]
- if parent, exists := visited[e.node]; !exists || parent == "" {
- visited[e.node] = e.parent
- for child := range g.outputs[e.node] {
- S = append(S, edge{child, e.node})
- }
- }
- if e.node == seed && e.parent != "" {
- result := []string{}
- node := e.parent
- for node != seed {
- result = append(result, node)
- node = visited[node]
- }
- result = append(result, seed)
- // reverse
- for left, right := 0, len(result)-1; left < right; left, right = left+1, right-1 {
- result[left], result[right] = result[right], result[left]
- }
- return result
- }
- }
- return []string{}
- }
- // FindParents returns the other ends of incoming edges.
- func (g *Graph) FindParents(to string) []string {
- result := []string{}
- for node, children := range g.outputs {
- if _, exists := children[to]; exists {
- result = append(result, node)
- }
- }
- return result
- }
- // FindChildren returns the other ends of outgoing edges.
- func (g *Graph) FindChildren(from string) []string {
- result := []string{}
- for child := range g.outputs[from] {
- result = append(result, child)
- }
- sort.Strings(result)
- return result
- }
- // Serialize outputs the graph in Graphviz format.
- func (g *Graph) Serialize(sorted []string) string {
- node2index := map[string]int{}
- for index, node := range sorted {
- node2index[node] = index
- }
- var buffer bytes.Buffer
- buffer.WriteString("digraph Hercules {\n")
- nodesFrom := []string{}
- for nodeFrom := range g.outputs {
- nodesFrom = append(nodesFrom, nodeFrom)
- }
- sort.Strings(nodesFrom)
- for _, nodeFrom := range nodesFrom {
- links := []string{}
- for nodeTo := range g.outputs[nodeFrom] {
- links = append(links, nodeTo)
- }
- sort.Strings(links)
- for _, nodeTo := range links {
- buffer.WriteString(fmt.Sprintf(" \"%d %s\" -> \"%d %s\"\n",
- node2index[nodeFrom], nodeFrom, node2index[nodeTo], nodeTo))
- }
- }
- buffer.WriteString("}")
- return buffer.String()
- }
- // DebugDump converts the graph to a string. As the name suggests, useful for debugging.
- func (g *Graph) DebugDump() string {
- S := make([]string, 0, len(g.outputs))
- for n := range g.outputs {
- if g.inputs[n] == 0 {
- S = append(S, n)
- }
- }
- sort.Strings(S)
- var buffer bytes.Buffer
- buffer.WriteString(strings.Join(S, " ") + "\n")
- keys := []string{}
- vals := map[string][]string{}
- for key, val1 := range g.outputs {
- val2 := make([]string, len(val1))
- for name, idx := range val1 {
- val2[idx-1] = name
- }
- keys = append(keys, key)
- vals[key] = val2
- }
- sort.Strings(keys)
- for _, key := range keys {
- buffer.WriteString(fmt.Sprintf("%s %d = ", key, g.inputs[key]))
- outs := vals[key]
- buffer.WriteString(strings.Join(outs, " ") + "\n")
- }
- return buffer.String()
- }
|