package graphimport ()// TopologicalSort runs a topological sort on a given directed graph and returns// the vertex hashes in topological order. The topological order is a non-unique// order of vertices in a directed graph where an edge from vertex A to vertex B// implies that vertex A appears before vertex B.//// Note that TopologicalSort doesn't make any guarantees about the order. If there// are multiple valid topological orderings, an arbitrary one will be returned.// To make the output deterministic, use [StableTopologicalSort].//// TopologicalSort only works for directed acyclic graphs. This implementation// works non-recursively and utilizes Kahn's algorithm.func [ comparable, any]( Graph[, ]) ([], error) {if !.Traits().IsDirected {returnnil, fmt.Errorf("topological sort cannot be computed on undirected graph") } , := .Order()if != nil {returnnil, fmt.Errorf("failed to get graph order: %w", ) } , := .PredecessorMap()if != nil {returnnil, fmt.Errorf("failed to get predecessor map: %w", ) } := make([], 0)for , := range {iflen() == 0 { = append(, ) } } := make([], 0, ) := make(map[]struct{}, )forlen() > 0 { := [0] = [1:]if , := []; {continue } = append(, ) [] = struct{}{}for , := range {delete(, )iflen() == 0 { = append(, ) } } }iflen() != {returnnil, errors.New("topological sort cannot be computed on graph with cycles") }return , nil}// StableTopologicalSort does the same as [TopologicalSort], but takes a function// for comparing (and then ordering) two given vertices. This allows for a stable// and deterministic output even for graphs with multiple topological orderings.func [ comparable, any]( Graph[, ], func(, ) bool) ([], error) {if !.Traits().IsDirected {returnnil, fmt.Errorf("topological sort cannot be computed on undirected graph") } , := .PredecessorMap()if != nil {returnnil, fmt.Errorf("failed to get predecessor map: %w", ) } := make([], 0) := make(map[]struct{})for , := range {iflen() == 0 { = append(, ) [] = struct{}{} } } := make([], 0, len()) := make(map[]struct{})sort.Slice(, func(, int) bool {return ([], []) })forlen() > 0 { := [0] = [1:]if , := []; {continue } = append(, ) [] = struct{}{} := make([], 0)for , := range {delete(, )iflen() != 0 {continue }if , := []; {continue } = append(, ) [] = struct{}{} }sort.Slice(, func(, int) bool {return ([], []) }) = append(, ...) } , := .Order()if != nil {returnnil, fmt.Errorf("failed to get graph order: %w", ) }iflen() != {returnnil, errors.New("topological sort cannot be computed on graph with cycles") }return , nil}// TransitiveReduction returns a new graph with the same vertices and the same// reachability as the given graph, but with as few edges as possible. The graph// must be a directed acyclic graph.//// TransitiveReduction is a very expensive operation scaling with O(V(V+E)).func [ comparable, any]( Graph[, ]) (Graph[, ], error) {if !.Traits().IsDirected {returnnil, fmt.Errorf("transitive reduction cannot be performed on undirected graph") } , := .Clone()if != nil {returnnil, fmt.Errorf("failed to clone the graph: %w", ) } , := .AdjacencyMap()if != nil {returnnil, fmt.Errorf("failed to get adajcency map: %w", ) }// For each vertex in the graph, run a depth-first search from each direct // successor of that vertex. Then, for each vertex visited within the DFS, // inspect all of its edges. Remove the edges that also appear in the edge // set of the top-level vertex and target the current vertex. These edges // are redundant because their targets apparently are not only reachable // from the top-level vertex, but also through a DFS.for , := range { , := .Order()if != nil {returnnil, fmt.Errorf("failed to get graph order: %w", ) }for := range { := make([], 0, ) := make(map[]struct{}, ) := make(map[]bool, ) = append(, )forlen() > 0 { := [len()-1] = [:len()-1]if , := []; { [] = falsecontinue } [] = struct{}{} [] = true = append(, )iflen([]) == 0 { [] = false }for := range [] {if , := []; {if [] {// If the current adjacency is both on the stack and // has already been visited, there is a cycle.returnnil, fmt.Errorf("transitive reduction cannot be performed on graph with cycle") }continue }if , := [][]; { _ = .RemoveEdge(, ) } = append(, ) } } } }return , nil}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.