diff --git a/graph/depthfirstsearch.go b/graph/depthfirstsearch.go index a4afb7647..c035c79f5 100644 --- a/graph/depthfirstsearch.go +++ b/graph/depthfirstsearch.go @@ -18,7 +18,7 @@ func NotExist(target int, slice []int) bool { return true } -func DepthFirstSearch(start, end int, nodes []int, edges [][]bool) ([]int, bool) { +func DepthFirstSearchHelper(start, end int, nodes []int, edges [][]bool, showroute bool) ([]int, bool) { var route []int var stack []int startIdx := GetIdx(start, nodes) @@ -42,7 +42,16 @@ func DepthFirstSearch(start, end int, nodes []int, edges [][]bool) ([]int, bool) return route, true } } - return nil, false + + if showroute { + return route, false + } else { + return nil, false + } +} + +func DepthFirstSearch(start, end int, nodes []int, edges [][]bool) ([]int, bool) { + return DepthFirstSearchHelper(start, end, nodes, edges, false) } // func main() { diff --git a/graph/topological.go b/graph/topological.go new file mode 100644 index 000000000..89fe46c42 --- /dev/null +++ b/graph/topological.go @@ -0,0 +1,35 @@ +package graph + +// Assumes that graph given is valid and possible to +// get a topo ordering. +// constraints are array of []int{a, b}, representing +// an edge going from a to b +func Topological(N int, constraints [][]int) []int { + dependencies := make([]int, N) + nodes := make([]int, N) + for i := range nodes { + nodes[i] = i + } + edges := make([][]bool, N) + for i := range edges { + edges[i] = make([]bool, N) + } + + for _, c := range constraints { + a := c[0] + b := c[1] + dependencies[b]++ + edges[a][b] = true + } + + ans := []int{} + for s := 0; s < N; s++ { + // Only start walking from top level nodes + if dependencies[s] == 0 { + route, _ := DepthFirstSearchHelper(s, N, nodes, edges, true) + ans = append(ans, route...) + } + } + + return ans +} diff --git a/graph/topological_test.go b/graph/topological_test.go new file mode 100644 index 000000000..2a914e0ae --- /dev/null +++ b/graph/topological_test.go @@ -0,0 +1,59 @@ +package graph + +import ( + "testing" +) + +var testCases = []struct { + name string + N int + constraints [][]int +}{ + { + "basic test", 2, + [][]int{{1, 0}}, + }, + { + "double path", 7, + [][]int{ + {0, 1}, {1, 3}, {3, 5}, + {0, 2}, {2, 4}, {4, 6}}, + }, + { + "star shape", 7, + [][]int{ + {0, 1}, {0, 3}, {0, 5}, + {0, 2}, {0, 4}, {0, 6}}, + }, + { + "tree shape", 7, + [][]int{ + {0, 1}, {1, 3}, {1, 5}, + {0, 2}, {2, 4}, {2, 6}}, + }, +} + +func TestTopological(t *testing.T) { + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actual := Topological(tc.N, tc.constraints) + + visited := make([]bool, tc.N) + positions := make([]int, tc.N) + for i := 0; i < tc.N; i++ { + positions[actual[i]] = i + visited[actual[i]] = true + } + for _, v := range visited { + if !v { + t.Errorf("nodes not all visited, %v", visited) + } + } + for _, c := range tc.constraints { + if positions[c[0]] > positions[c[1]] { + t.Errorf("%v dun satisfy %v", actual, c) + } + } + }) + } +}