Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## [Unreleased]

### Added
- **Tarjan's SCC algorithm** (`tarjan_scc.hpp`) — single-pass O(V+E) strongly connected components using iterative DFS with low-link values; no transpose graph needed
- 17 new Tarjan SCC tests (`test_tarjan_scc.cpp`)
- **Mapped (sparse) graph algorithm support** — all 14 algorithms now accept `adjacency_list<G>` (both index and map-based containers)
- `mapped_vertex_range`, `mapped_adjacency_list`, `mapped_bidirectional_adjacency_list` concepts
- `vertex_property_map<G, T>` type alias and `make_vertex_property_map` factory (vector for index graphs, unordered_map for mapped)
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
- **Header-only** — drop into any CMake project; no compiled components
- **Generic** - Enabled by the use of descriptors.
- **Works with your graphs** — Bring your own graph. `std::vector<std::vector<int>>` and `std::map<int, std::list<std::pair<int,double>>>` are also valid graphs out of the box.
- **13 algorithms** — Dijkstra, Bellman-Ford, BFS, DFS, topological sort, connected components, articulation points, biconnected components, MST, triangle counting, MIS, label propagation, Jaccard coefficient
- **14 algorithms** — Dijkstra, Bellman-Ford, BFS, DFS, topological sort, connected components, Tarjan SCC, articulation points, biconnected components, MST, triangle counting, MIS, label propagation, Jaccard coefficient
- **7 lazy views** — vertexlist, edgelist, incidence, neighbors, BFS, DFS, topological sort — all composable with range adaptors
- **Bidirectional edge access** — `in_edges`, `in_degree`, reverse BFS/DFS/topological sort via `in_edge_accessor`
- **Customization Point Objects (CPOs)** — adapt existing data structures without modifying them
Expand Down Expand Up @@ -96,7 +96,7 @@ Both share a common descriptor system and customization-point interface.

| Category | What's Included | Details |
|----------|-----------------|---------|
| **Algorithms** | Dijkstra, Bellman-Ford, BFS, DFS, topological sort, connected components, articulation points, biconnected components, MST, triangle counting, MIS, label propagation, Jaccard | [Algorithm reference](docs/status/implementation_matrix.md#algorithms) |
| **Algorithms** | Dijkstra, Bellman-Ford, BFS, DFS, topological sort, connected components, Tarjan SCC, articulation points, biconnected components, MST, triangle counting, MIS, label propagation, Jaccard | [Algorithm reference](docs/status/implementation_matrix.md#algorithms) |
| **Views** | vertexlist, edgelist, incidence, neighbors, BFS, DFS, topological sort | [View reference](docs/status/implementation_matrix.md#views) |
| **Containers** | `dynamic_graph` (27 trait combos), `compressed_graph` (CSR), `undirected_adjacency_list` | [Container reference](docs/status/implementation_matrix.md#containers) |
| **CPOs** | 19 customization point objects (vertices, edges, target_id, vertex_value, edge_value, …) | [CPO reference](docs/reference/cpo-reference.md) |
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/algorithm-complexity.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ headers for every algorithm in graph-v3.
|-----------|----------|------|-------|-------------------|--------|
| **Connected Components** | `connected_components` | O(V+E) | O(V) | `index_adjacency_list` | `connected_components.hpp` |
| **Kosaraju (SCC)** | `kosaraju` | O(V+E) | O(V) | `index_adjacency_list` (graph + transpose) | `connected_components.hpp` |
| **Tarjan (SCC)** | `tarjan_scc` | O(V+E) | O(V) | `adjacency_list` | `tarjan_scc.hpp` |
| **Afforest** | `afforest` | O(V + E·α(V)) | O(V) | `index_adjacency_list` | `connected_components.hpp` |
| **Biconnected Components** | `biconnected_components` | O(V+E) | O(V+E) | `index_adjacency_list` | `biconnected_components.hpp` |

Expand Down Expand Up @@ -96,6 +97,7 @@ algorithm/
├── jaccard.hpp
├── label_propagation.hpp
├── mst.hpp
├── tarjan_scc.hpp
├── topological_sort.hpp
└── traversal_common.hpp (shared types: visitors, init helpers)
```
Expand Down
3 changes: 2 additions & 1 deletion docs/status/implementation_matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

## Algorithms

13 implemented algorithms in `include/graph/algorithm/` (excluding `traversal_common.hpp`):
14 implemented algorithms in `include/graph/algorithm/` (excluding `traversal_common.hpp`):

| Algorithm | Header | Test File | Status |
|-----------|--------|-----------|--------|
Expand All @@ -25,6 +25,7 @@
| Depth-first search | `depth_first_search.hpp` | `test_depth_first_search.cpp` | Implemented |
| Topological sort | `topological_sort.hpp` | `test_topological_sort.cpp` | Implemented |
| Connected components (Kosaraju SCC) | `connected_components.hpp` | `test_connected_components.cpp`, `test_scc_bidirectional.cpp` | Implemented |
| Tarjan SCC | `tarjan_scc.hpp` | `test_tarjan_scc.cpp` | Implemented |
| Articulation points | `articulation_points.hpp` | `test_articulation_points.cpp` | Implemented |
| Biconnected components | `biconnected_components.hpp` | `test_biconnected_components.cpp` | Implemented |
| MST (Prim / Kruskal) | `mst.hpp` | `test_mst.cpp` | Implemented |
Expand Down
2 changes: 2 additions & 0 deletions docs/user-guide/algorithms.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ All headers are under `include/graph/algorithm/`.
| [Articulation Points](algorithms/articulation_points.md) | `articulation_points.hpp` | Cut vertices whose removal disconnects the graph | O(V+E) | O(V) |
| [Biconnected Components](algorithms/biconnected_components.md) | `biconnected_components.hpp` | Maximal 2-connected subgraphs (Hopcroft-Tarjan) | O(V+E) | O(V+E) |
| [Connected Components](algorithms/connected_components.md) | `connected_components.hpp` | Undirected CC, directed SCC (Kosaraju), union-find (afforest) | O(V+E) | O(V) |
| [Tarjan SCC](algorithms/tarjan_scc.md) | `tarjan_scc.hpp` | Single-pass directed SCC via low-link values | O(V+E) | O(V) |

**Minimum Spanning Trees**

Expand Down Expand Up @@ -145,6 +146,7 @@ All headers are under `include/graph/algorithm/`.
| [Maximal Independent Set](algorithms/mis.md) | Analytics | `mis.hpp` | O(V+E) | O(V) |
| [Prim MST](algorithms/mst.md#prims-algorithm) | MST | `mst.hpp` | O(E log V) | O(V) |
| [Topological Sort](algorithms/topological_sort.md) | Traversal | `topological_sort.hpp` | O(V+E) | O(V) |
| [Tarjan SCC](algorithms/tarjan_scc.md) | Components | `tarjan_scc.hpp` | O(V+E) | O(V) |
| [Triangle Count](algorithms/triangle_count.md) | Analytics | `tc.hpp` | O(m^{3/2}) | O(1) |

---
Expand Down
5 changes: 5 additions & 0 deletions docs/user-guide/algorithms/connected_components.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ map-based (sparse vertex ID) graphs are supported.
|-----------|----------|----------|
| `connected_components` | Undirected graphs | DFS-based |
| `kosaraju` | Directed graphs (SCC) | Two DFS passes (requires transpose) |
| `tarjan_scc` | Directed graphs (SCC) | Single DFS pass (no transpose needed) |
| `afforest` | Large graphs, parallel-friendly | Union-find with neighbor sampling |

All three fill a `component` array where `component[v]` is the component ID for
Expand All @@ -56,6 +57,9 @@ vertex v.
the number of components directly.
- **`kosaraju`** — when you need strongly connected components of a directed
graph. Requires constructing the transpose graph (all edges reversed).
- **`tarjan_scc`** — when you need SCCs without constructing a transpose graph.
Single-pass DFS using low-link values. Returns the number of SCCs.
See [Tarjan SCC](tarjan_scc.md).
- **`afforest`** — when working with large graphs or when you intend to
parallelize later. Uses union-find with neighbor sampling, which has good
cache behavior on large inputs.
Expand Down Expand Up @@ -313,6 +317,7 @@ compress(comp);

## See Also

- [Tarjan SCC](tarjan_scc.md) — single-pass SCC algorithm (no transpose needed)
- [Biconnected Components](biconnected_components.md) — maximal 2-connected subgraphs
- [Articulation Points](articulation_points.md) — cut vertices
- [Algorithm Catalog](../algorithms.md) — full list of algorithms
Expand Down
169 changes: 169 additions & 0 deletions docs/user-guide/algorithms/tarjan_scc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<table><tr>
<td><img src="../../assets/logo.svg" width="120" alt="graph-v3 logo"></td>
<td>

# Tarjan's Strongly Connected Components

</td>
</tr></table>

> [← Back to Algorithm Catalog](../algorithms.md)

## Table of Contents
- [Overview](#overview)
- [When to Use](#when-to-use)
- [Include](#include)
- [Algorithm](#algorithm)
- [Parameters](#parameters)
- [Supported Graph Properties](#supported-graph-properties)
- [Examples](#examples)
- [Mandates](#mandates)
- [Preconditions](#preconditions)
- [Effects](#effects)
- [Returns](#returns)
- [Throws](#throws)
- [Complexity](#complexity)
- [See Also](#see-also)

## Overview

Tarjan's algorithm finds all strongly connected components (SCCs) in a directed
graph using a single depth-first search. It uses discovery times and low-link
values to identify SCC roots, then pops completed SCCs from an auxiliary stack.

Unlike Kosaraju's algorithm, Tarjan's requires **no transpose graph** and performs
only **one DFS pass**, making it simpler to use when a transpose is unavailable.

| Property | Value |
|----------|-------|
| Passes | 1 (single DFS) |
| Transpose needed | No |
| SCC order | Reverse topological |
| Return value | Number of SCCs |

## When to Use

- **Use `tarjan_scc`** when you need SCCs and don't have (or don't want to
construct) a transpose graph. Single-pass, no extra graph required.
- **Use `kosaraju`** when you already have a transpose or bidirectional graph,
or need topological SCC ordering.
- **Use `connected_components`** for undirected graphs.

## Include

```cpp
#include <graph/algorithm/tarjan_scc.hpp>
```

## Algorithm

```cpp
size_t tarjan_scc(G&& g, ComponentFn&& component);
```

Single-pass iterative DFS using low-link values. Fills `component(g, uid)` with
the SCC ID for each vertex and returns the total number of SCCs.

## Parameters

| Parameter | Description |
|-----------|-------------|
| `g` | Graph satisfying `adjacency_list` |
| `component` | Callable `(const G&, vertex_id_t<G>) -> ComponentID&` returning a mutable reference. For containers: wrap with `container_value_fn(comp)`. Must satisfy `vertex_property_fn_for<ComponentFn, G>`. |

**Return value:** `size_t` — number of strongly connected components.

## Supported Graph Properties

**Directedness:**
- ✅ Directed graphs (required)
- ❌ Undirected graphs (use `connected_components` instead)

**Edge Properties:**
- ✅ Weighted edges (weights ignored)
- ✅ Self-loops (handled correctly)
- ✅ Multi-edges (treated as single edge)
- ✅ Cycles

**Graph Structure:**
- ✅ Connected graphs
- ✅ Disconnected graphs (processes all components)
- ✅ Empty graphs (returns 0)

**Container Requirements:**
- Required: `adjacency_list<G>`
- `component` must satisfy `vertex_property_fn_for<ComponentFn, G>`

## Examples

### Example 1: Basic SCC Detection

```cpp
#include <graph/algorithm/tarjan_scc.hpp>
#include <graph/container/dynamic_graph.hpp>
#include <vector>

using Graph = container::dynamic_graph<void, void, void, uint32_t, false,
container::vov_graph_traits<void>>;

// Directed graph: 0→1→2→0 (cycle = 1 SCC), 2→3 (singleton SCC)
Graph g({{0, 1}, {1, 2}, {2, 0}, {2, 3}});

std::vector<uint32_t> comp(num_vertices(g));
size_t num_scc = tarjan_scc(g, container_value_fn(comp));

// num_scc == 2
// comp[0] == comp[1] == comp[2] (cycle forms one SCC)
// comp[3] != comp[0] (3 is a singleton SCC)
```

### Example 2: Comparing with Kosaraju

```cpp
// Same graph, both algorithms
std::vector<uint32_t> comp_tarjan(num_vertices(g));
std::vector<uint32_t> comp_kosaraju(num_vertices(g));

size_t n = tarjan_scc(g, container_value_fn(comp_tarjan));
kosaraju(g, g_transpose, container_value_fn(comp_kosaraju));

// Both find the same SCCs (component IDs may differ, but groupings match)
```

## Mandates

- `G` must satisfy `adjacency_list<G>`
- `ComponentFn` must satisfy `vertex_property_fn_for<ComponentFn, G>`

## Preconditions

- `component(g, uid)` must be valid for all vertex IDs in `g`

## Effects

- Writes SCC IDs via `component(g, uid)` for all vertices
- Does not modify the graph `g`

## Returns

`size_t` — the number of strongly connected components.

## Throws

- `std::bad_alloc` if internal allocations fail
- Exception guarantee: Basic. Graph `g` remains unchanged; component output may be partial.

## Complexity

| Metric | Value |
|--------|-------|
| Time | O(V + E) |
| Space | O(V) |

## See Also

- [Connected Components](connected_components.md) — Kosaraju SCC, undirected CC, afforest
- [Articulation Points](articulation_points.md) — cut vertices (also uses Tarjan-style low-link)
- [Biconnected Components](biconnected_components.md) — maximal 2-connected subgraphs
- [Algorithm Catalog](../algorithms.md) — full list of algorithms
- [test_tarjan_scc.cpp](../../../tests/algorithms/test_tarjan_scc.cpp) — test suite
Loading
Loading