Skip to content

Commit

Permalink
add directed acyclic graph to handle module dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
weidonglian committed Dec 9, 2024
1 parent f81428a commit b540de2
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 34 deletions.
10 changes: 8 additions & 2 deletions algorithms/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ add_executable(test-algorithms
tree/ordered_map.cpp
tree/visit_tree.cpp
tree/tree_manipulate.cpp
tree/directed_graph.cpp
tree/directed_acyclic_graph.cpp
tree/flatten_bst_into_linked_list.cpp
hash_table/separate_chainning.cpp
set/set_key.cpp
Expand All @@ -41,7 +41,13 @@ if(OpenMP_CXX_FOUND)
set(LINK_LIB_OPENMP OpenMP::OpenMP_CXX)
endif()

target_link_libraries(test-algorithms Threads::Threads Catch2::Catch2WithMain rapidcheck simple_svg ${LINK_LIB_OPENMP})
target_link_libraries(test-algorithms
Threads::Threads
Catch2::Catch2WithMain
rapidcheck
simple_svg
absl::inlined_vector
${LINK_LIB_OPENMP})

target_include_directories(test-algorithms PRIVATE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>")
add_test(NAME test-algorithms COMMAND test-algorithms)
106 changes: 106 additions & 0 deletions algorithms/tree/directed_acyclic_graph.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#include <cstdlib>
#include <queue>

#include "absl/container/inlined_vector.h"
#include "base.hpp"

namespace {

template <typename T>
struct node {
T val; // node value
int degree; // number of edges connected to parent node
absl::InlinedVector<T, 8> children; // children nodes
};

template <typename T>
class graph {
public:
graph() : nodes() {}

void add_node(const T& val) {
if (!nodes.contains(val)) {
auto& n = nodes[val];
n.val = val;
n.degree = 0;
}
}

void add_edge(const T& from, const T& to) {
add_node(from);
add_node(to);
nodes[from].children.push_back(to);
nodes[to].degree++;
}

// Topological sort returns nodes in order of their level.
// Throws an exception if the graph has a cycle.
// Otherwise, returns nodes in topological order.
// Nodes are sorted by value per level for deterministic results.
std::vector<T> topological_sort() const {
std::vector<T> result;
std::map<T, int> current_degrees;
std::queue<T> queue;
// Group nodes by their level
for (const auto& [val, n] : nodes) {
current_degrees[val] = n.degree;
if (n.degree == 0) {
queue.push(val);
}
}

absl::InlinedVector<T, 8> sorted_children;
while (!queue.empty()) {
auto val = queue.front();
queue.pop();
result.push_back(val);

sorted_children.clear();
for (const auto& adj : nodes.at(val).children) {
if (--current_degrees[adj] == 0) {
sorted_children.push_back(adj);
}
}
std::sort(sorted_children.begin(), sorted_children.end());
for (const auto& adj : sorted_children) {
queue.push(adj);
}
}

if (result.size() != nodes.size()) {
throw std::runtime_error("graph has cycle.");
}

return result;
}

private:
std::map<T, node<T>> nodes;
};

} // namespace

TEST_CASE("dag_has_cycle", "[tree]") {
graph<int> g;
g.add_edge(1, 2);
g.add_edge(2, 3);
g.add_edge(3, 4);
g.add_edge(4, 1);
CHECK_THROWS(g.topological_sort());
}

TEST_CASE("dag_topological_sort", "[tree]") {
graph<int> g;
g.add_edge(1, 2);
g.add_edge(2, 3);
g.add_edge(3, 4);
g.add_node(5); // isolated node
g.add_edge(7, 6);
REQUIRE(g.topological_sort() == std::vector<int>({1, 5, 7, 2, 6, 3, 4}));
}

TEST_CASE("dag_topological_sort_single_node", "[tree]") {
graph<int> g;
g.add_node(5); // isolated node
REQUIRE(g.topological_sort() == std::vector<int>({5}));
}
30 changes: 0 additions & 30 deletions algorithms/tree/directed_graph.cpp

This file was deleted.

2 changes: 1 addition & 1 deletion algorithms/tree/tree_manipulate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include "base.hpp"

struct tree_node {
tree_node(int value) : value_(value), left_(nullptr), right_(nullptr) {}
explicit tree_node(int value) : value_(value), left_(nullptr), right_(nullptr) {}
~tree_node() {
delete left_;
delete right_;
Expand Down
2 changes: 1 addition & 1 deletion algorithms/tree/visit_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace {

class vt_node {
public:
vt_node(std::string name) : name_(std::move(name)), left_(nullptr), right_(nullptr) {}
explicit vt_node(std::string name) : name_(std::move(name)), left_(nullptr), right_(nullptr) {}

~vt_node() {
delete left_;
Expand Down

0 comments on commit b540de2

Please sign in to comment.