Dalam sebuah jaringan komputer, server mana yang bila gagal akan memutus komunikasi antar segmen? Dalam jalan raya, jembatan mana yang bila runtuh membuat dua kota terisolasi? Pertanyaan-pertanyaan ini memetakan ke persoalan Articulation Points (titik kritis) dan Bridges (jembatan kritis) pada graph. Articulation point adalah node yang bila dihapus meningkatkan jumlah komponen terhubung. Bridge adalah edge yang bila dihapus menyebabkan hal serupa. Algoritma klasik menggunakan DFS dengan dua array kunci — disc[] (discovery time) dan low[] (lowlink) — yang sama seperti pada Tarjan SCC. Pada Chapter ini kita akan membedah mekanisme lowlink, mengimplementasikan dalam C++, dan melihat kapan sebuah edge adalah bridge dan kapan sebuah node adalah articulation point.
Aturan: Definisi Formal #
Pada graph tak berarah terhubung G = (V, E):
- Articulation Point (cut vertex): node
usehingga menghapusu(dan semua edge terkait) meningkatkan jumlah connected component. - Bridge (cut edge): edge
esehingga menghapusemeningkatkan jumlah connected component.
- Articulation point dan bridge hanya bermakna pada graph tak berarah. Untuk graph berarah, konsep ekuivalen adalah strong bridge dan strong articulation point — jauh lebih kompleks (varian algoritma lowlink).
Mekanisme: DFS Lowlink #
Inisialisasi DFS dari setiap node belum dikunjungi. Untuk setiap node u:
disc[u]= discovery time (urutan kunjungan, incrementing counter).low[u]= discovery time minimum yang dapat dicapai dari subtreeumelalui paling banyak satu back edge (edge ke ancestor di DFS tree).
Selama DFS dari u, untuk setiap tetangga v:
vbelum dikunjungi → tree edge. Rekursi kev, lalu updatelow[u] = min(low[u], low[v]).vsudah dikunjungi danv != parent[u]→ back edge. Updatelow[u] = min(low[u], disc[v]).
Kondisi Articulation Point #
Node u adalah articulation point jika:
- Root dengan ≥ 2 anak tree (children dalam DFS tree), ATAU
- Non-root dengan anak
vsehinggalow[v] >= disc[u]— subtreevtidak punya back edge ke ancestoru.
Kondisi Bridge #
Edge (u, v) adalah bridge jika dan hanya jika low[v] > disc[u] (subtree v tidak punya back edge ke u atau ancestor-nya; bahkan tidak ke u sendiri). Perhatikan > bukan >= — bila low[v] == disc[u], ada edge balik ke u sendiri, sehingga u masih bisa diakses tanpa edge (u,v).
Implementasi dengan DFS Lowlink #
#include <iostream>
#include <vector>
#include <algorithm>
#include <set>
class Graph {
int n;
std::vector<std::vector<int>> adj;
int timer = 0;
std::vector<int> disc, low;
std::vector<bool> visited;
std::set<int> articulation_pts;
std::vector<std::pair<int,int>> bridges;
public:
explicit Graph(int n) : n(n), adj(n), disc(n, -1), low(n, -1), visited(n, false) {}
void add_edge(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
void find_ap_bridges() {
for (int u = 0; u < n; ++u) {
if (!visited[u]) {
dfs(u, -1);
}
}
}
void print_results() {
std::cout << "Articulation Points: ";
if (articulation_pts.empty()) std::cout << "(tidak ada)";
for (int x : articulation_pts) std::cout << x << ' ';
std::cout << '\n';
std::cout << "Bridges: ";
if (bridges.empty()) std::cout << "(tidak ada)";
for (auto [u, v] : bridges) std::cout << "(" << u << "-" << v << ") ";
std::cout << '\n';
}
private:
void dfs(int u, int parent) {
visited[u] = true;
disc[u] = low[u] = timer++;
int children = 0;
bool is_articulation = false;
for (int v : adj[u]) {
if (!visited[v]) {
children++;
dfs(v, u);
low[u] = std::min(low[u], low[v]);
// Articulation point (non-root)
if (parent != -1 && low[v] >= disc[u]) {
is_articulation = true;
}
// Bridge
if (low[v] > disc[u]) {
bridges.push_back({u, v});
}
} else if (v != parent) {
// Back edge
low[u] = std::min(low[u], disc[v]);
}
}
// Root dengan >= 2 children = articulation point
if (parent == -1 && children >= 2) {
is_articulation = true;
}
if (is_articulation) articulation_pts.insert(u);
}
};
int main() {
// Graph:
// 1 - 2 - 3
// | | |
// 0 4 - 5
// |
// 6
Graph g(7);
g.add_edge(0, 1);
g.add_edge(0, 6);
g.add_edge(1, 2);
g.add_edge(2, 3);
g.add_edge(2, 4);
g.add_edge(3, 5);
g.add_edge(4, 5);
g.find_ap_bridges();
g.print_results();
std::cout << "\nPenjelasan:\n";
std::cout << " AP 0: menghapus 0 memutus 6 dari graph (0 satu-satunya tetangga 6)\n";
std::cout << " AP 1: menghapus 1 memutus {0,6} dari {2,3,4,5}\n";
std::cout << " AP 2: menghapus 2 memutus {0,1,6} dari {3,4,5}\n";
std::cout << " Bridge (0,6): satu-satunya edge ke 6\n";
std::cout << " Bridge (0,1): 0 hanya terhubung ke 1 dan 6; hapus ini isolasi {0,6}\n";
std::cout << " Bridge (1,2): hapus ini memutus {0,1,6} dari {2,3,4,5}\n";
std::cout << " Edge (2,3),(2,4),(3,5),(4,5) bukan bridge karena cycle 2-3-5-4-2\n";
return 0;
}Output yang Dihasilkan:
Articulation Points: 0 1 2
Bridges: (1-2) (0-1) (0-6)
Penjelasan:
AP 0: menghapus 0 memutus 6 dari graph (0 satu-satunya tetangga 6)
AP 1: menghapus 1 memutus {0,6} dari {2,3,4,5}
AP 2: menghapus 2 memutus {0,1,6} dari {3,4,5}
Bridge (0,6): satu-satunya edge ke 6
Bridge (0,1): 0 hanya terhubung ke 1 dan 6; hapus ini isolasi {0,6}
Bridge (1,2): hapus ini memutus {0,1,6} dari {2,3,4,5}
Edge (2,3),(2,4),(3,5),(4,5) bukan bridge karena cycle 2-3-5-4-2Bedah Kode Baris-per-Baris #
std::vector<int> disc, low;: dua array kunci.disc[u]= discovery time,low[u]= lowlink. Inisialisasi-1= belum dikunjungi.std::set<int> articulation_pts: gunakansetuntuk menghindari duplikasi (node bisa dideteksi beberapa kali dari anak yang berbeda).std::vector<std::pair<int,int>> bridges: list bridge. Edge unik karena DFS tree edge unik.disc[u] = low[u] = timer++: set discovery time dan lowlink awal. Lowlink awalnya = discovery (belum ada back edge).int children = 0; bool is_articulation = false;: hitung anak tree dan status articulation. Untuk root, anak ≥ 2 = AP; untuk non-root, periksa kondisilow[v] >= disc[u].if (!visited[v])→ tree edge: rekursi kev, lalu perbaruilow[u]denganlow[v]. Subtreevmungkin memiliki back edge yang menjangkau ancestoru— infonya diteruskan keu.if (parent != -1 && low[v] >= disc[u]): kondisi AP non-root.low[v] >= disc[u]berarti subtreevtidak punya back edge ke ancestoru— hapusuakan memutusvdari graph.if (low[v] > disc[u]): kondisi bridge. Strict>(bukan>=): bilalow[v] == disc[u], ada edge balik keusendiri, jadi hapus edge(u,v)masih meninggalkan jalur alternatif.else if (v != parent)→ back edge:vsudah dikunjungi tapi bukan parent → edge balik ke ancestoru. Updatelow[u] = min(low[u], disc[v])— pakaidisc[v]bukanlow[v](penting untuk kebenaran).if (parent == -1 && children >= 2) is_articulation = true;: kondisi root. Root dengan ≥ 2 anak tree pasti AP karena hapus root → dua subtree tidak terhubung.- Hasil: 3 AP
{0, 1, 2}dan 3 bridge{(0,6), (0,1), (1,2)}. Edge-edge dalam cycle2-3-5-4-2tidak terdeteksi sebagai bridge karena ada jalur alternatif.
Trace untuk Node 2 (Non-root) #
DFS dari 0: disc[0] = low[0] = 0. Anak: 1 (tree edge).
DFS dari 1: disc[1] = low[1] = 1. Anak: 2 (tree edge).
DFS dari 2: disc[2] = low[2] = 2. Anak: 3, 4 (tree edges).
- DFS dari 3:
disc[3] = low[3] = 3. Tetangga: 2 (parent, skip), 5 (tree edge).- DFS dari 5:
disc[5] = low[5] = 4. Tetangga: 3 (parent, skip), 4 (tree edge).- DFS dari 4:
disc[4] = low[4] = 5. Tetangga: 2 (visited, ≠ parent 5 → back edge). Updatelow[4] = min(5, disc[2]=2) = 2. Tetangga: 5 (parent, skip). Selesai, return.
- DFS dari 4:
- Kembali ke 5:
low[5] = min(4, low[4]=2) = 2. Kondisi AP non-root:low[4] = 2 >= disc[5] = 4? Tidak. Kondisi bridge:low[4] = 2 > disc[5] = 4? Tidak. Selesai.
- DFS dari 5:
- Kembali ke 3:
low[3] = min(3, low[5]=2) = 2. AP:low[5]=2 >= disc[3]=3? Tidak. Bridge:low[5]=2 > disc[3]=3? Tidak. - DFS dari 4: sudah visited, skip di iterasi 2.
- Kembali ke 2: setelah kedua anak diproses,
low[2] = min(2, low[3]=2, low[4]=2) = 2. Anak 3: AP non-root?low[3]=2 >= disc[2]=2? Ya → AP. Anak 4: AP non-root?low[4]=2 >= disc[2]=2? Ya → AP (duplikat, set menyatukan). Bridge:low[3]=2 > disc[2]=2? Tidak.low[4]=2 > disc[2]=2? Tidak. Edge (2,3) dan (2,4) bukan bridge.
✓ Sesuai hasil: 2 adalah AP, tetapi edge (2,3) dan (2,4) bukan bridge.
Kompleksitas #
| Aspek | Nilai | Catatan |
|---|---|---|
| Waktu | O(V + E) | DFS tunggal, setiap edge diperiksa konstan. |
| Ruang | O(V + E) | Adj list + array disc, low, stack. |
| Multi-graph | Bekerja | Perlu hati-hati: edge paralel selalu bukan bridge. |
| Self-loop | Tidak masuk hitungan bridge | v != parent skip. |
Variasi dan Penerapan #
- Biconnected components: kelompok edge di mana setiap dua edge berbagi cycle. Bridge = edge yang membentuk BCC sendirian.
- 2-edge-connected: graph tanpa bridge. Tambah edge minimum untuk membuat 2-edge-connected.
- 2-vertex-connected: graph tanpa articulation point.
- Network reliability: AP dan bridge = titik/edge kritis yang gagalnya memutus layanan.
- Topological analysis: pada graph terhubung, AP dan bridge mengidentifikasi weak points.
Aplikasi Nyata #
- Network design: identifikasi server (AP) dan kabel (bridge) kritis.
- Transportation: jembatan jalan raya yang runtuh mengisolasi daerah.
- Circuit analysis: AP dan bridge pada PCB graph = komponen kritis.
- Social network: AP = user yang menjadi penghubung dua komunitas.
- Biological networks: AP = protein yang tanpanya jaringan rusak.
Ringkasan #
- Articulation Point: node yang bila dihapus meningkatkan jumlah komponen.
- Bridge: edge yang bila dihapus meningkatkan jumlah komponen.
- Algoritma: DFS dengan
disc[]danlow[](lowlink). - AP non-root:
low[v] >= disc[u]untuk suatu anakv. AP root: ≥ 2 anak tree. - Bridge:
low[v] > disc[u](strict>). - Kompleksitas
O(V + E)waktu dan ruang. - Aplikasi: network reliability, transport, social network analysis.
Bab berikutnya akan membahas topik terakhir Section ini: Max-Flow dan Min-Cut — persoalan aliran maksimum pada jaringan dengan kapasitas, dan teorema max-flow min-cut yang menghubungkan dua konsep fundamental.