Skip to content

Commit 2c41eb9

Browse files
committed
non-recursive dfs
1 parent 9ab3609 commit 2c41eb9

File tree

1 file changed

+65
-23
lines changed

1 file changed

+65
-23
lines changed

cp-algo/graph/tarjan.hpp

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -37,31 +37,73 @@ namespace cp_algo::graph {
3737
template<template<typename> class Context, graph_type graph>
3838
auto tarjan(graph const& g) {
3939
Context<graph> context(g);
40-
auto dfs = [&](this auto &&dfs, node_index v, edge_index ep = -1) -> void {
41-
context.state[v] = visiting;
42-
context.tin[v] = context.low[v] = context.timer++;
43-
context.stack.push(v);
44-
for(auto e: g.outgoing(v)) {
45-
if constexpr (undirected_graph_type<graph>) {
46-
if (ep == graph::opposite_idx(e)) {
47-
continue;
48-
}
40+
41+
auto const& adj = g.incidence_lists();
42+
43+
struct frame {
44+
node_index v;
45+
edge_index ep;
46+
int sv; // edge index in stack_union
47+
enum { INIT, PROCESS_EDGES, HANDLE_CHILD } state;
48+
};
49+
50+
std::stack<frame> dfs_stack;
51+
52+
for (auto root: g.nodes()) {
53+
if (context.state[root] != unvisited) continue;
54+
55+
dfs_stack.push({root, -1, 0, frame::INIT});
56+
57+
while (!dfs_stack.empty()) {
58+
auto& f = dfs_stack.top();
59+
60+
if (f.state == frame::INIT) {
61+
context.state[f.v] = visiting;
62+
context.tin[f.v] = context.low[f.v] = context.timer++;
63+
context.stack.push(f.v);
64+
f.sv = adj.head[f.v];
65+
f.state = frame::PROCESS_EDGES;
4966
}
50-
node_index u = g.edge(e).to;
51-
if (context.state[u] == unvisited) {
52-
dfs(u, e);
53-
context.low[v] = std::min(context.low[v], context.low[u]);
54-
context.on_tree_edge(v, u);
55-
} else if (context.state[u] != blocked) {
56-
context.low[v] = std::min(context.low[v], context.tin[u]);
67+
68+
if (f.state == frame::HANDLE_CHILD) {
69+
auto e = adj.data[f.sv];
70+
f.sv = adj.next[f.sv];
71+
node_index u = g.edge(e).to;
72+
context.low[f.v] = std::min(context.low[f.v], context.low[u]);
73+
context.on_tree_edge(f.v, u);
74+
f.state = frame::PROCESS_EDGES;
5775
}
58-
}
59-
context.state[v] = visited;
60-
context.on_exit(v);
61-
};
62-
for (auto v: g.nodes()) {
63-
if (context.state[v] == unvisited) {
64-
dfs(v);
76+
77+
// PROCESS_EDGES
78+
bool found_child = false;
79+
while (f.sv != 0) {
80+
auto e = adj.data[f.sv];
81+
82+
if constexpr (undirected_graph_type<graph>) {
83+
if (f.ep == graph::opposite_idx(e)) {
84+
f.sv = adj.next[f.sv];
85+
continue;
86+
}
87+
}
88+
89+
node_index u = g.edge(e).to;
90+
if (context.state[u] == unvisited) {
91+
f.state = frame::HANDLE_CHILD;
92+
dfs_stack.push({u, e, 0, frame::INIT});
93+
found_child = true;
94+
break;
95+
} else if (context.state[u] != blocked) {
96+
context.low[f.v] = std::min(context.low[f.v], context.tin[u]);
97+
}
98+
f.sv = adj.next[f.sv];
99+
}
100+
101+
if (found_child) continue;
102+
103+
// All edges processed
104+
context.state[f.v] = visited;
105+
context.on_exit(f.v);
106+
dfs_stack.pop();
65107
}
66108
}
67109
return context.components;

0 commit comments

Comments
 (0)