Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
kosaraju_example.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
224#include <iostream>
225#include <iomanip>
226#include <string>
227#include <vector>
228#include <cstring>
229
230#include <tpl_graph.H>
231#include <kosaraju.H>
232#include <Tarjan.H> // For comparison
233
234using namespace std;
235using namespace Aleph;
236
237// =============================================================================
238// Graph Type Definitions
239// =============================================================================
240
244
245// =============================================================================
246// Utility Functions
247// =============================================================================
248
249Node* find_node(Graph& g, const string& name)
250{
251 for (auto it = g.get_node_it(); it.has_curr(); it.next())
252 if (it.get_curr()->get_info() == name)
253 return it.get_curr();
254 return nullptr;
255}
256
258{
259 cout << "Graph structure (" << g.get_num_nodes() << " nodes, "
260 << g.get_num_arcs() << " arcs):\n";
261 for (auto nit = g.get_node_it(); nit.has_curr(); nit.next())
262 {
263 auto* node = nit.get_curr();
264 cout << " " << node->get_info() << " → ";
265 bool first = true;
266 for (auto ait = g.get_out_it(node); ait.has_curr(); ait.next())
267 {
268 auto* arc = ait.get_curr();
269 auto* tgt = g.get_tgt_node(arc);
270 if (!first) cout << ", ";
271 cout << tgt->get_info();
272 first = false;
273 }
274 if (first) cout << "(none)";
275 cout << "\n";
276 }
277}
278
279// =============================================================================
280// Example 1: Basic SCC Detection
281// =============================================================================
282
284{
285 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
286 cout << "Example 1: Basic Strongly Connected Components\n";
287 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
288
289 /*
290 * Graph with 3 SCCs:
291 *
292 * SCC1: {A, B, C} SCC2: {D, E} SCC3: {F}
293 *
294 * A ←──── B D ←── E
295 * │ ↑ │ ↑
296 * └──→ C ─┘ └──→ ─┘
297 * │ │
298 * └────────────→───────┘
299 * │
300 * ↓
301 * F
302 *
303 * Arcs between SCCs: C→E, E→F
304 */
305
306 Graph g;
307
308 // SCC 1: A, B, C form a cycle
309 auto* A = g.insert_node("A");
310 auto* B = g.insert_node("B");
311 auto* C = g.insert_node("C");
312
313 g.insert_arc(A, C);
314 g.insert_arc(C, B);
315 g.insert_arc(B, A);
316
317 // SCC 2: D, E form a cycle
318 auto* D = g.insert_node("D");
319 auto* E = g.insert_node("E");
320
321 g.insert_arc(D, E);
322 g.insert_arc(E, D);
323
324 // SCC 3: F is alone
325 auto* F = g.insert_node("F");
326
327 // Cross-component edges
328 g.insert_arc(C, E); // SCC1 → SCC2
329 g.insert_arc(E, F); // SCC2 → SCC3
330
331 print_graph(g);
332
333 cout << "\n▶ Running Kosaraju's Algorithm:\n\n";
334
337
339
340 cout << " Found " << sccs.size() << " strongly connected components:\n\n";
341
342 int scc_num = 1;
343 for (auto& scc : sccs)
344 {
345 cout << " SCC " << scc_num++ << ": { ";
346 bool first = true;
347 for (auto it = scc.get_node_it(); it.has_curr(); it.next())
348 {
349 if (!first) cout << ", ";
350 // Get the original node via cookie mapping
351 auto scc_node = it.get_curr();
352 auto orig_node = static_cast<Node*>(NODE_COOKIE(scc_node));
353 cout << orig_node->get_info();
354 first = false;
355 }
356 cout << " }\n";
357 cout << " Internal arcs: " << scc.get_num_arcs() << "\n\n";
358 }
359
360 cout << " Cross-component arcs (" << cross_arcs.size() << "):\n";
361 for (auto* arc : cross_arcs)
362 {
363 auto* src = g.get_src_node(arc);
364 auto* tgt = g.get_tgt_node(arc);
365 cout << " " << src->get_info() << " → " << tgt->get_info() << "\n";
366 }
367}
368
369// =============================================================================
370// Example 2: Using the Lightweight Version
371// =============================================================================
372
374{
375 cout << "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
376 cout << "Example 2: Lightweight SCC Detection (Node Lists Only)\n";
377 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
378
379 cout << "When you only need to know which nodes belong to which component,\n";
380 cout << "the lightweight version is more efficient (no subgraph construction).\n\n";
381
382 // Same graph as before
383 Graph g;
384
385 auto* A = g.insert_node("A");
386 auto* B = g.insert_node("B");
387 auto* C = g.insert_node("C");
388 auto* D = g.insert_node("D");
389 auto* E = g.insert_node("E");
390 auto* F = g.insert_node("F");
391
392 g.insert_arc(A, C);
393 g.insert_arc(C, B);
394 g.insert_arc(B, A);
395 g.insert_arc(D, E);
396 g.insert_arc(E, D);
397 g.insert_arc(C, E);
398 g.insert_arc(E, F);
399
400 cout << "▶ Running lightweight Kosaraju:\n\n";
401
403
404 cout << " Found " << sccs.size() << " components:\n\n";
405
406 int scc_num = 1;
407 for (auto& component : sccs)
408 {
409 cout << " Component " << scc_num++ << ": { ";
410 bool first = true;
411 for (auto* node : component)
412 {
413 if (!first) cout << ", ";
414 cout << node->get_info();
415 first = false;
416 }
417 cout << " }\n";
418 }
419}
420
421// =============================================================================
422// Example 3: Strongly Connected Graph
423// =============================================================================
424
426{
427 cout << "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
428 cout << "Example 3: Fully Strongly Connected Graph\n";
429 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
430
431 /*
432 * A graph is strongly connected if there is exactly ONE SCC
433 * containing all vertices.
434 *
435 * A ←───── B
436 * │↘ ↗│
437 * │ ↘ ↗ │
438 * ↓ ✕ ↓
439 * │ ↗ ↘ │
440 * │↗ ↘↓│
441 * C ─────→ D
442 */
443
444 Graph g;
445
446 auto* A = g.insert_node("A");
447 auto* B = g.insert_node("B");
448 auto* C = g.insert_node("C");
449 auto* D = g.insert_node("D");
450
451 // Create edges so that every vertex can reach every other
452 g.insert_arc(A, C);
453 g.insert_arc(A, D);
454 g.insert_arc(B, A);
455 g.insert_arc(B, D);
456 g.insert_arc(C, B);
457 g.insert_arc(D, C);
458
459 print_graph(g);
460
462
463 cout << "\n▶ Result:\n\n";
464 cout << " Number of SCCs: " << sccs.size() << "\n";
465
466 if (sccs.size() == 1)
467 cout << " ✓ The graph is STRONGLY CONNECTED\n";
468 else
469 cout << " ✗ The graph is NOT strongly connected\n";
470}
471
472// =============================================================================
473// Example 4: DAG (No SCCs with more than one node)
474// =============================================================================
475
477{
478 cout << "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
479 cout << "Example 4: Directed Acyclic Graph (DAG)\n";
480 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
481
482 cout << "In a DAG, every SCC contains exactly one node (no cycles).\n\n";
483
484 /*
485 * A ────→ B ────→ D
486 * │ │ │
487 * └──→ C ←┘ ↓
488 * E
489 */
490
491 Graph g;
492
493 auto* A = g.insert_node("A");
494 auto* B = g.insert_node("B");
495 auto* C = g.insert_node("C");
496 auto* D = g.insert_node("D");
497 auto* E = g.insert_node("E");
498
499 g.insert_arc(A, B);
500 g.insert_arc(A, C);
501 g.insert_arc(B, C);
502 g.insert_arc(B, D);
503 g.insert_arc(D, E);
504
505 print_graph(g);
506
508
509 cout << "\n▶ Result:\n\n";
510 cout << " Number of SCCs: " << sccs.size() << "\n";
511 cout << " Number of nodes: " << g.get_num_nodes() << "\n";
512
513 if (sccs.size() == g.get_num_nodes())
514 cout << "\n ✓ This is a DAG (each node is its own SCC)\n";
515}
516
517// =============================================================================
518// Example 5: Comparison with Tarjan's Algorithm
519// =============================================================================
520
522{
523 cout << "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
524 cout << "Example 5: Kosaraju vs Tarjan's Algorithm\n";
525 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
526
527 cout << R"(
528┌────────────────────────────────────────────────────────────────────────┐
529│ SCC Algorithm Comparison │
530├────────────────────────────────────────────────────────────────────────┤
531│ Aspect │ Kosaraju │ Tarjan │
532├────────────────────┼────────────────────┼──────────────────────────────┤
533│ DFS passes │ 2 │ 1 │
534│ Extra space │ O(V+E) for G^T │ O(V) for stack │
535│ Time complexity │ O(V + E) │ O(V + E) │
536│ Implementation │ Simpler │ More complex │
537│ Order of SCCs │ Reverse topo order │ Any order │
538├────────────────────┴────────────────────┴──────────────────────────────┤
539│ When to use Kosaraju: │
540│ • Need SCCs in reverse topological order │
541│ • Simpler implementation preferred │
542│ • Memory not critical (need space for transposed graph) │
543│ │
544│ When to use Tarjan: │
545│ • Memory is critical (no transposed graph needed) │
546│ • Only one DFS pass preferred │
547│ • Already have Tarjan's implementation for other purposes │
548└────────────────────────────────────────────────────────────────────────┘
549)";
550
551 // Quick verification that both give same number of components
552 Graph g;
553
554 auto* A = g.insert_node("A");
555 auto* B = g.insert_node("B");
556 auto* C = g.insert_node("C");
557 auto* D = g.insert_node("D");
558
559 g.insert_arc(A, B);
561 g.insert_arc(C, A); // Creates cycle A-B-C
562 g.insert_arc(C, D);
563
565
569
570 cout << "\n Verification on sample graph:\n";
571 cout << " Kosaraju found: " << kosaraju_sccs.size() << " SCCs\n";
572 cout << " Tarjan found: " << tarjan_blks.size() << " SCCs\n";
575 cout << " ✓ Both algorithms agree!\n";
576}
577
578// =============================================================================
579// Example 6: Applications
580// =============================================================================
581
583{
584 cout << "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
585 cout << "Example 6: Real-World Applications of SCCs\n";
586 cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
588 cout << R"(
589 1. CIRCULAR DEPENDENCY DETECTION
590 ─────────────────────────────
591 In build systems (Make, CMake) or package managers (npm, pip),
592 an SCC with more than one node indicates circular dependencies.
593
594 2. 2-SAT SOLVER
595 ────────────
596 Boolean satisfiability with clauses of 2 literals can be solved
597 in O(V+E) using SCC decomposition of the implication graph.
598
599 3. SOCIAL NETWORK ANALYSIS
600 ───────────────────────
601 SCCs identify tightly-knit communities where information flows
602 freely in both directions between all members.
603
604 4. WEB PAGE RANKING
605 ────────────────
606 SCCs help identify clusters of web pages that link to each other,
607 useful in understanding website structure.
608
609 5. COMPILER OPTIMIZATION
610 ─────────────────────
611 SCCs in data flow graphs help identify loops and enable
612 optimizations like loop-invariant code motion.
613
614 6. DATABASE QUERY OPTIMIZATION
615 ──────────────────────────
616 Finding cycles in query dependency graphs helps detect
617 and handle recursive queries.
618)";
619}
620
621// =============================================================================
622// Main
623// =============================================================================
624
625static void usage(const char* prog)
626{
627 cout << "Usage: " << prog << " [--compare] [--help]\n";
628 cout << "\nIf no flags are given, all demos are executed.\n";
629}
630
631static bool has_flag(int argc, char* argv[], const char* flag)
632{
633 for (int i = 1; i < argc; ++i)
634 if (std::strcmp(argv[i], flag) == 0)
635 return true;
636 return false;
637}
638
639int main(int argc, char* argv[])
640{
641 if (has_flag(argc, argv, "--help"))
642 {
643 usage(argv[0]);
644 return 0;
645 }
646
647 const bool compare = has_flag(argc, argv, "--compare");
648
649 cout << "╔══════════════════════════════════════════════════════════════════════╗\n";
650 cout << "║ Kosaraju's Algorithm for Strongly Connected Components ║\n";
651 cout << "╚══════════════════════════════════════════════════════════════════════╝\n\n";
652
653 if (compare)
654 {
656 cout << "\nDone.\n";
657 return 0;
658 }
659
663 example_dag();
666
667 cout << "\nDone.\n";
668 return 0;
669}
Tarjan's algorithm for strongly connected components.
int main()
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3848
typename BaseGraph::Arc Arc
Definition graph-dry.H:3852
typename BaseGraph::Node Node
Definition graph-dry.H:3851
Dynamic singly linked list with functional programming support.
Definition htlist.H:1423
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:731
Out_Iterator get_out_it(Node *p) const noexcept
Return an output iterator on the incoming nodes to p
Definition graph-dry.H:3099
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:695
constexpr size_t get_num_arcs() const noexcept
Definition graph-dry.H:778
auto get_node_it() const noexcept
Obtains an iterator to the nodes of graph.
Definition graph-dry.H:2780
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:737
void kosaraju_connected_components(const GT &g, DynList< GT > &blk_list, DynList< typename GT::Arc * > &arc_list)
Compute strongly connected components using Kosaraju's algorithm.
Definition kosaraju.H:210
#define NODE_COOKIE(p)
Return the node cookie
Kosaraju's algorithm for finding strongly connected components.
void print_graph(Graph &g)
void example_strongly_connected()
void example_applications()
void example_basic_sccs()
static void usage(const char *prog)
Node * find_node(Graph &g, const string &name)
void example_comparison_tarjan()
static bool has_flag(int argc, char *argv[], const char *flag)
void example_lightweight_version()
void example_dag()
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
DynList< T > maps(const C &c, Op op)
Classic map operation.
STL namespace.
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Generic graph and digraph implementations.