diff --git a/README.md b/README.md index aba4400..0c89f20 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,6 @@ Checkout the [contribution guidelines](https://github.com/Artris/algorithms/blob | Karatsuba Multiplication | [Lec 11](https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/lecture-videos/lecture-11-integer-arithmetic-karatsuba-multiplication/) | | | Breadth-First Search (BFS) | [Lec 13](https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/lecture-videos/lecture-13-breadth-first-search-bfs/) | [graphs / BreadthFirstSearch.re](https://github.com/Artris/algorithms/blob/master/src/graphs/BreadthFirstSearch.re) | | Depth-First Search (DFS) | [Lec 14](https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/lecture-videos/lecture-14-depth-first-search-dfs-topological-sort/) | [graphs / DepthFirstSearch.re](https://github.com/Artris/algorithms/blob/master/src/graphs/DepthFirstSearch.re) | -| Topological Sort | [Lec 14](https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/lecture-videos/lecture-14-depth-first-search-dfs-topological-sort/) | | +| Topological Sort | [Lec 14](https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/lecture-videos/lecture-14-depth-first-search-dfs-topological-sort/) | [graphs / TopologicalSort.re](https://github.com/Artris/algorithms/blob/master/src/graphs/TopologicalSort.re) | | Dijkstra | [Lec 16](https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/lecture-videos/lecture-16-dijkstra/) | | | Bellman-Ford | [Lec 17](https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/lecture-videos/lecture-17-bellman-ford/) | | \ No newline at end of file diff --git a/__tests__/topological_sort_test.re b/__tests__/topological_sort_test.re new file mode 100644 index 0000000..bd957ee --- /dev/null +++ b/__tests__/topological_sort_test.re @@ -0,0 +1,57 @@ +open Jest; +open Expect; + +describe("Topological Sort", () => { + open TopologicalSort; + + test("single node", () => { + let adj_list = [{id: "A", children: []}]; + let expected_sorting = ["A"]; + let sorting = sort(adj_list); + + expect(sorting) |> toEqual(expected_sorting); + }); + + test("simple tree", () => { + + let adj_list = [ + {id: "A", children: ["B"]}, + {id: "B", children: ["C"]}, + {id: "C", children: ["D"]}, + {id: "D", children: []} + ]; + + let expected_sorting = ["A", "B", "C", "D"]; + let sorting = sort(adj_list); + + expect(sorting) |> toEqual(expected_sorting); + }); + + test("star graph", () => { + + let adj_list = [ + {id: "A", children: []}, + {id: "B", children: ["A"]}, + {id: "C", children: ["A"]}, + {id: "D", children: ["A"]}]; + + let expected_sorting = ["D", "C", "B", "A"]; + let sorting = sort(adj_list); + + expect(sorting) |> toEqual(expected_sorting); + }); + + test("forest", () => { + + let adj_list = [ + {id: "A", children: ["B"]}, + {id: "B", children: []}, + {id: "C", children: ["D"]}, + {id: "D", children: []}]; + + let expected_sorting = ["C", "D", "A", "B"]; + let sorting = sort(adj_list); + + expect(sorting) |> toEqual(expected_sorting); + }); +}); \ No newline at end of file diff --git a/src/graphs/TopologicalSort.re b/src/graphs/TopologicalSort.re new file mode 100644 index 0000000..3e71e59 --- /dev/null +++ b/src/graphs/TopologicalSort.re @@ -0,0 +1,73 @@ +type node = { + id: string, + children: list(string), +}; + +type directedGraph = list(node); + +exception Invalid_node_id(string); + +exception Graph_not_DAG; + +let parseAdjList = adj_list => { + let adj_tbl = Hashtbl.create(List.length(adj_list)); + let insert = ({id, children}) => { + Hashtbl.add(adj_tbl, id, children); + }; + + List.iter(insert, adj_list); + let validateChildren = node => { + List.iter(child_id => { + if (!Hashtbl.mem(adj_tbl, child_id)) { + raise(Invalid_node_id(child_id)); + } + }, node.children); + }; + + List.iter(validateChildren, adj_list); + adj_tbl; +}; + +let rec visit = (~node_id, ~adj_tbl, ~visited, ~ordering, ~ancestors) => { + if (Hashtbl.mem(visited, node_id)) { + let pred = id => id == node_id; + switch (List.find(pred, ancestors)) { + | exception Not_found => ordering; + | _ => raise(Graph_not_DAG); + }; + } + else { + let ancestors = List.append(ancestors, [node_id]); + let children = Hashtbl.find(adj_tbl, node_id); + let visitChild = (ordering, child_id) => { + visit( + ~node_id=child_id, + ~adj_tbl=adj_tbl, + ~visited=visited, + ~ordering=ordering, + ~ancestors=ancestors); + }; + + Hashtbl.add(visited, node_id, node_id); + let list = List.fold_left(visitChild, ordering, children); + List.append(list, [node_id]); + }; +}; + +let sort = adj_list => { + let num_nodes = List.length(adj_list); + let adj_tbl = parseAdjList(adj_list); + let visited = Hashtbl.create(num_nodes); + + let traverse = (ordering, node) => { + visit( + ~node_id=node.id, + ~adj_tbl=adj_tbl, + ~visited=visited, + ~ordering=ordering, + ~ancestors=[]); + }; + + let top_sorting = List.fold_left(traverse, [], adj_list); + List.rev(top_sorting); +}; \ No newline at end of file diff --git a/src/graphs/TopologicalSort.rei b/src/graphs/TopologicalSort.rei new file mode 100644 index 0000000..2845975 --- /dev/null +++ b/src/graphs/TopologicalSort.rei @@ -0,0 +1,8 @@ +type node = { + id: string, + children: list(string), +}; + +type directedGraph = list(node); + +let sort: directedGraph => list(string); \ No newline at end of file