diff --git a/include/graph/algorithm/connected_components.hpp b/include/graph/algorithm/connected_components.hpp index 9e6ed38..3459dbe 100644 --- a/include/graph/algorithm/connected_components.hpp +++ b/include/graph/algorithm/connected_components.hpp @@ -1,4 +1,4 @@ -/** +/** * @file connected_components.hpp * * @brief Single-Source Shortest paths and shortest sistances algorithms using Dijkstra & @@ -21,7 +21,6 @@ #include "graph/views/breadth_first_search.hpp" #include #include -#include #ifndef GRAPH_CC_HPP # define GRAPH_CC_HPP @@ -94,25 +93,32 @@ size_t connected_components(G&& g, // graph Component& component // out: connected component assignment ) { size_t N(size(vertices(g))); - std::vector visited(N, false); using CT = typename std::decay::type; std::fill(component.begin(), component.end(), std::numeric_limits::max()); + std::stack> S; CT cid = 0; - for (auto&& [uid, u] : views::vertexlist(g)) { - if (visited[uid]) { + for (vertex_id_t uid = 0; uid < N; ++uid) { + if (component[uid] < std::numeric_limits::max()) { continue; } - visited[uid] = true; - component[uid] = cid; - if (!size(edges(g, u))) { - ++cid; + + if (!size(edges(g, uid))) { + component[uid] = cid++; continue; } - vertices_breadth_first_search_view bfs(g, uid); - for (auto&& [vid, v] : bfs) { - component[vid] = cid; - visited[vid] = true; + + component[uid] = cid; + S.push(uid); + while (!S.empty()) { + auto vid = S.top(); + S.pop(); + for (auto&& [wid, vw] : views::incidence(g, vid)) { + if (component[wid] == std::numeric_limits::max()) { + component[wid] = cid; + S.push(wid); + } + } } ++cid; } @@ -156,7 +162,7 @@ template static vertex_id_t sample_frequent_element(Component& component, size_t num_samples = 1024) { std::unordered_map counts(32); std::mt19937 gen; - std::uniform_int_distribution distribution(0, static_cast(component.size() - 1)); + std::uniform_int_distribution distribution(0, component.size() - 1); for (size_t i = 0; i < num_samples; ++i) { vertex_id_t sample = distribution(gen); @@ -172,9 +178,9 @@ template requires random_access_range> && integral> && std::convertible_to, vertex_id_t> && std::convertible_to, range_value_t> -size_t afforest(G&& g, // graph - Component& component, // out: connected component assignment - const size_t neighbor_rounds = 2) { +void afforest(G&& g, // graph + Component& component, // out: connected component assignment + const size_t neighbor_rounds = 2) { size_t N(size(vertices(g))); std::iota(component.begin(), component.end(), 0); @@ -205,34 +211,16 @@ size_t afforest(G&& g, // graph } compress(component); - vertex_id_t target_id = 0; - std::map, vertex_id_t> reindex; - for (vertex_id_t vtx = 0; vtx < N; ++vtx) { - if (!reindex.empty()) { - auto it = reindex.find(component[vtx]); - if (it != reindex.end()) { - component[vtx] = (*it).second; - } - } else if (component[vtx] == target_id) { - ++target_id; - } else if (component[vtx] > target_id) { - reindex.insert(pair(component[vtx], target_id)); - component[vtx] = target_id; - ++target_id; - } - } - - return target_id; } template requires random_access_range> && integral> && std::convertible_to, vertex_id_t> && std::convertible_to, range_value_t> -size_t afforest(G&& g, // graph - GT&& g_t, // graph transpose - Component& component, // out: connected component assignment - const size_t neighbor_rounds = 2) { +void afforest(G&& g, // graph + GT&& g_t, // graph transpose + Component& component, // out: connected component assignment + const size_t neighbor_rounds = 2) { size_t N(size(vertices(g))); std::iota(component.begin(), component.end(), 0); @@ -266,24 +254,6 @@ size_t afforest(G&& g, // graph } compress(component); - vertex_id_t target_id = 0; - std::map, vertex_id_t> reindex; - for (vertex_id_t vtx = 0; vtx < N; ++vtx) { - if (!reindex.empty()) { - auto it = reindex.find(component[vtx]); - if (it != reindex.end()) { - component[vtx] = (*it).second; - } - } else if (component[vtx] == target_id) { - ++target_id; - } else if (component[vtx] > target_id) { - reindex.insert(pair(component[vtx], target_id)); - component[vtx] = target_id; - ++target_id; - } - } - - return target_id; } } // namespace graph diff --git a/tests/cc_tests.cpp b/tests/cc_tests.cpp index 256b30a..f1bd0cd 100644 --- a/tests/cc_tests.cpp +++ b/tests/cc_tests.cpp @@ -98,9 +98,13 @@ TEST_CASE("afforest test", "[afforest cc]") { gt.load_edges(reverse, edge_proj, N); std::vector> component(size(vertices(g))); - auto components = graph::afforest(g, component); + graph::afforest(g, component); + std::unordered_set> componentIds; + for (vertex_id_t vtx = 0; vtx < N; ++vtx) { + componentIds.insert(component[vtx]); + } + auto components = componentIds.size(); REQUIRE(components == 3); - REQUIRE(*std::ranges::max_element(component) == 2); } TEST_CASE("afforest test weak", "[afforest weak_cc]") { @@ -131,9 +135,14 @@ TEST_CASE("afforest test weak", "[afforest weak_cc]") { gt.load_edges(reverse, edge_proj, N); std::vector> component(size(vertices(g))); - auto components = graph::afforest(g, gt, component); + graph::afforest(g, gt, component); + std::unordered_set> componentIds; + for (vertex_id_t vtx = 0; vtx < N; ++vtx) { + componentIds.insert(component[vtx]); + } + auto components = componentIds.size(); + REQUIRE(components == 1); - REQUIRE(*std::ranges::max_element(component) == 0); } #endif