From ca84b2020a3dd0c12b696e878ba0f3de2862bd68 Mon Sep 17 00:00:00 2001 From: Niranjan Godbole Date: Tue, 3 Oct 2017 14:20:09 +0530 Subject: [PATCH] adds topological sort --- sorts/TopologicalSort.go | 115 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 sorts/TopologicalSort.go diff --git a/sorts/TopologicalSort.go b/sorts/TopologicalSort.go new file mode 100644 index 0000000..3e9d741 --- /dev/null +++ b/sorts/TopologicalSort.go @@ -0,0 +1,115 @@ +package main + +import ( + "fmt" +) + +type Dependency struct { + nodeId int + children []int +} + +// converts dependencies to edges +func dependenciesToEdges(dependencies []Dependency) []Edge { + edges := []Edge{} + for _, node := range dependencies { + for _, child := range node.children { + edges = append(edges, Edge{start: node.nodeId, end: child}) + } + } + return edges +} + +type Edge struct { + start int // start and end are node ids + end int +} + +type Node struct { + id int + children []int + selected bool + incoming int +} + +// takes a list of edges as input with start and end node_ids +// useful for cases like task ordering +func topologicalSort(input []Edge) ([]int, [][]int) { + + // generate a mapping for node_id and number of incoming edges + incoming := make(map[int]int) + for _, edge := range input { + if _, ok := incoming[edge.start]; !ok { + incoming[edge.start] = 0 + } + incoming[edge.end] += 1 + } + + nodes := make([]*Node, 0) + for k, v := range incoming { + chdn := []int{} + for _, edge := range input { + if edge.start == k { + chdn = append(chdn, edge.end) + } + } + nodes = append(nodes, &Node{id: k, children: chdn, selected: false, incoming: v}) + } + + res := []int{} + var levels [][]int + for len(res) < len(nodes) { + level := []int{} + queue := []int{} + for idx, node := range nodes { + // select a node with no incoming dependency + if !node.selected && node.incoming == 0 { + queue = append(queue, idx) + nodes[idx].selected = true + } + } + for _, idx := range queue { + node := nodes[idx] + level = append(level, node.id) + // decrement incoming values for all children + for _, childId := range node.children { + for _, child := range nodes { + if !child.selected && child.id == childId { + child.incoming-- + } + } + } + } + res = append(res, level...) + levels = append(levels, level) + } + return res, levels +} + +func main() { + var edges []Edge + edges = append(edges, Edge{1, 2}) + edges = append(edges, Edge{2, 3}) + edges = append(edges, Edge{4, 3}) + edges = append(edges, Edge{4, 5}) + edges = append(edges, Edge{3, 6}) + edges = append(edges, Edge{5, 6}) + + order, levels := topologicalSort(edges) + fmt.Println("result: ", order) + fmt.Println("levels: ", levels) + + // or you can specify dependencies + var deps []Dependency + deps = append(deps, Dependency{nodeId: 1, children: []int{2}}) + deps = append(deps, Dependency{nodeId: 2, children: []int{3}}) + deps = append(deps, Dependency{nodeId: 4, children: []int{3, 5}}) + deps = append(deps, Dependency{nodeId: 5, children: []int{6}}) + deps = append(deps, Dependency{nodeId: 3, children: []int{6}}) + + edges = dependenciesToEdges(deps) + order, levels = topologicalSort(edges) + fmt.Println("result: ", order) + fmt.Println("levels: ", levels) + +}