|
| 1 | +#ifndef CP_ALGO_GRAPH_TARJAN_HPP |
| 2 | +#define CP_ALGO_GRAPH_TARJAN_HPP |
| 3 | +#include "base.hpp" |
| 4 | +#include "../structures/csr.hpp" |
| 5 | +#include <algorithm> |
| 6 | +#include <cassert> |
| 7 | +namespace cp_algo::graph { |
| 8 | + enum node_state { unvisited, visiting, visited, blocked }; |
| 9 | + template<graph_type graph> |
| 10 | + struct tarjan_context { |
| 11 | + big_vector<int> tin, low; |
| 12 | + big_vector<node_state> state; |
| 13 | + graph const* g; |
| 14 | + int timer; |
| 15 | + structures::csr<node_index> components; |
| 16 | + tarjan_context(graph const& g): |
| 17 | + tin(g.n()), low(g.n()), state(g.n()), g(&g), timer(0) { |
| 18 | + components.reserve_data(g.n()); |
| 19 | + } |
| 20 | + |
| 21 | + void on_tree_edge(node_index, edge_index) {} |
| 22 | + void on_exit(node_index) {} |
| 23 | + |
| 24 | + void collect(node_index v) { |
| 25 | + state[v] = blocked; |
| 26 | + components.push(v); |
| 27 | + for(auto e: g->outgoing(v)) { |
| 28 | + node_index u = g->edge(e).to; |
| 29 | + if (state[u] != blocked && state[u] != visiting) { |
| 30 | + collect(u); |
| 31 | + } |
| 32 | + } |
| 33 | + } |
| 34 | + }; |
| 35 | + template<template<typename> class Context, graph_type graph> |
| 36 | + auto tarjan(graph const& g) { |
| 37 | + Context<graph> context(g); |
| 38 | + auto dfs = [&](this auto &&dfs, node_index v, edge_index ep = -1) -> void { |
| 39 | + context.state[v] = visiting; |
| 40 | + context.tin[v] = context.low[v] = context.timer++; |
| 41 | + for(auto e: g.outgoing(v)) { |
| 42 | + if constexpr (undirected_graph_type<graph>) { |
| 43 | + if (ep == graph::opposite_idx(e)) { |
| 44 | + continue; |
| 45 | + } |
| 46 | + } |
| 47 | + node_index u = g.edge(e).to; |
| 48 | + if (context.state[u] == unvisited) { |
| 49 | + dfs(u, e); |
| 50 | + context.low[v] = std::min(context.low[v], context.low[u]); |
| 51 | + context.on_tree_edge(v, u); |
| 52 | + } else if (context.state[u] != blocked) { |
| 53 | + context.low[v] = std::min(context.low[v], context.tin[u]); |
| 54 | + } |
| 55 | + } |
| 56 | + context.state[v] = visited; |
| 57 | + context.on_exit(v); |
| 58 | + }; |
| 59 | + for (auto v: g.nodes()) { |
| 60 | + if (context.state[v] == unvisited) { |
| 61 | + dfs(v); |
| 62 | + } |
| 63 | + } |
| 64 | + return context.components; |
| 65 | + } |
| 66 | + template<graph_type graph> |
| 67 | + struct exit_context: tarjan_context<graph> { |
| 68 | + using base = tarjan_context<graph>; |
| 69 | + using base::base; |
| 70 | + |
| 71 | + void on_exit(node_index v) { |
| 72 | + if (base::low[v] == base::tin[v]) { |
| 73 | + base::components.new_row(); |
| 74 | + base::collect(v); |
| 75 | + } |
| 76 | + } |
| 77 | + }; |
| 78 | + // Tarjan's algorithm for Strongly Connected Components |
| 79 | + // returns components in reverse topological order |
| 80 | + template<digraph_type graph> |
| 81 | + auto strongly_connected_components(graph const& g) { |
| 82 | + return tarjan<exit_context>(g); |
| 83 | + } |
| 84 | + |
| 85 | + // Tarjan's algorithm for Two-Edge-Connected Components |
| 86 | + template<undirected_graph_type graph> |
| 87 | + auto two_edge_connected_components(graph const& g) { |
| 88 | + return tarjan<exit_context>(g); |
| 89 | + } |
| 90 | + |
| 91 | + template<undirected_graph_type graph> |
| 92 | + struct bcc_context: tarjan_context<graph> { |
| 93 | + using base = tarjan_context<graph>; |
| 94 | + using base::base; |
| 95 | + void on_tree_edge(node_index v, node_index u) { |
| 96 | + if (base::low[u] >= base::tin[v]) { |
| 97 | + base::components.new_row(); |
| 98 | + base::collect(u); |
| 99 | + base::components.push(v); |
| 100 | + } |
| 101 | + } |
| 102 | + void on_exit(node_index v) { |
| 103 | + if (std::empty(base::g->outgoing(v))) { |
| 104 | + base::components.new_row(); |
| 105 | + base::components.push(v); |
| 106 | + } |
| 107 | + } |
| 108 | + }; |
| 109 | + // Tarjan's algorithm for Biconnected Components (vertex-biconnected) |
| 110 | + template<undirected_graph_type graph> |
| 111 | + auto biconnected_components(graph const& g) { |
| 112 | + return tarjan<bcc_context>(g); |
| 113 | + } |
| 114 | +} |
| 115 | +#endif // CP_ALGO_GRAPH_TARJAN_HPP |
0 commit comments