Bridges (Low-Link)

Context: FIT2004_MOC · W5 applied advanced (D/HD tier) — the single-DFS answer to which edges hold the graph together ➔ one extra number per vertex turns “is this edge on a cycle?” into an comparison Parent Framework: Uninformed Search (BFS and DFS)

Quick Revision

  • 🎯 Objective: an edge is a bridge iff removing it raises the component count ➔ equivalently, iff it lies on no cycle ➔ testable in one DFS by asking whether the subtree below it can reach back above it.
  • 📦 Core Components: ➔ the visit counter | ➔ the earliest vertex the subtree at can reach without reusing an edge | the test .
  • ⚡ Key Constraint: the comparison is strict and mixed — of the child against of the parent. Equality means the child can reach itself, which is already a cycle, so the edge is not a bridge.

📝 How It Works

1. Correctness — Bridge On No Cycle (LO2)

  • Direction 1 ( is a bridge lies on no cycle) ➔ by contradiction. Suppose the bridge lies on a cycle. Take any path that used ; because is on a cycle there is a detour around it, so avoids entirely ⟹ deleting disconnects nothing, contradicting bridge.
  • Direction 2 ( on no cycle is a bridge) ➔ contrapositive. If is not a bridge, then and stay connected after deleting , so a path exists avoiding ; appending closes it ⟹ lies on a cycle.
  • What this buys ➔ the structural question becomes a reachability question, and reachability is what DFS already computes ➔ Cycle Detection.

2. The Two Quantities

  • ➔ the position of in DFS discovery order, — a timestamp saying how early was reached, so smaller means closer to the root.
  • ➔ the smallest reachable from using unused edges after was visited, formally
  • Plain English ➔ the earliest vertex the subtree hanging below can climb back up to, without walking back through the edge it came in on.
  • Both are per-vertex ➔ two integer arrays, auxiliary, alongside the DFS’s own stack.
  • The recurrence ➔ a vertex reachable from by a path of length is reachable from one of ‘s own unused neighbours, so the minimum propagates one hop at a time:
  • Where it is applied ➔ during the DFS itself: descend into each child, and on the way back up fold the child’s into the parent’s.
  • Why the cost does not move ➔ one per edge is extra work on a traversal that already inspects every edge ⟹ still ➔ Uninformed Search (BFS and DFS).
  • “Unused” is what excludes the parent edge ➔ marking the tree edge traversed before recursing stops the child from trivially “reaching back” to its parent along the same edge, which would make every equal to the root’s.

4. The Bridge Test

  • The criterion ➔ for a tree edge oriented in the direction DFS traversed it,
  • Reading it ➔ describes the earliest vertex can reach without reusing edges. If that is still later than , nothing below can climb back to or above it ⟹ no cycle contains ⟹ bridge.
  • Why equality fails the test ➔ means can reach by another route; that route plus is a cycle, so the edge is not a bridge.
  • Reaching strictly above ➔ means reaches an ancestor of , so the cycle is merely larger — still not a bridge.
  • Only tree edges are candidates ➔ a non-tree (back) edge always closes a cycle by construction, so it can never be a bridge; the test is applied exactly where DFS recursed.

⚙️ Core Implementation

🔹 FIND_BRIDGES — one DFS, two arrays

⚖️ Complexity

QuantityCostWhere it comes from
Time (adjacency list)one DFS; the fold is per edge
Time (adjacency matrix)the neighbour scan, not the algorithm ➔ Graph Representations
Auxiliary space recursion depth
Edge marksone traversed bit per edge — part of the graph, not auxiliary
Naive alternativedelete each edge, re-run a connectivity check
  • The gain is a factor of ➔ the low-link fold answers “is this edge on a cycle?” for all edges in the cost of one traversal, replacing separate connectivity tests.
  • Same shape as Cycle Detection ➔ both upgrade DFS’s per-vertex bookkeeping (a third state there, two counters here) rather than changing the traversal.

📊 Exam Execution Trace

Undirected : , , , . Adjacency lists in that order; DFS seeded at , counter starting at . The triangle hangs off by the single edge .

Manual Execution Trace

StepEvent afterBridge test
enter —
tree edge , enter pending
tree edge , enter pending
tree edge , enter pending
sees , already visited—back edge — never a bridge
return to —? no
return to —? no
sees , already visited—back edge
return to —? YES ➔ bridge

Final Extracted Output: the only bridge is . Every vertex of the triangle carries , i.e. we can all climb back to , so no triangle edge passes the strict test; cannot climb above itself to , so the edge that attaches the triangle is the cut edge — and deleting it does split into and .

⚠️ Common Mistakes

  • 💡 Testing a non-tree edge ➔ back edges close cycles by definition; run the comparison only where DFS actually recursed.
  • 💡 Using instead of ➔ turns every edge into a bridge at equality, and equality is exactly the case where a rival route home exists.
  • 💡 Claiming a bridge is any edge whose removal disconnects two specific vertices ➔ the definition is about the number of components rising, which is the same as no cycle containing it.
  • 💡 Storing low_link as a vertex label rather than a running minimum ➔ it must be re-folded on every return from a child, or the subtree’s reach never propagates upward.

🧠 Active Recall