Depth-First Search (DFS)
Terakhir diperbarui
Depth-first search adalah algoritma yang menjelajahi graf dengan masuk sedalam mungkin di setiap cabang sebelum backtrack (mundur). Dimulai dari sebuah node sumber, DFS mengunjungi satu tetangga, lalu tetangga dari tetangga itu, dan seterusnya sampai menemui jalan buntu (node tanpa tetangga yang belum dikunjungi). Pada titik itu DFS mundur dan mencoba cabang berikutnya. Tekan Putar di atas untuk melihatnya menyusuri satu jalur, mencapai dasar, lalu kembali keluar.
DFS paling alami ditulis dengan stack, entah stack eksplisit (seperti dalam animasi ini) atau call stack lewat rekursi. DFS mengunjungi setiap node dan edge sekali, jadi berjalan dalam waktu O(V + E) untuk graf dengan V vertex dan E edge.
Kompleksitas waktu dan ruang
| Ukuran | Kompleksitas | Catatan |
|---|---|---|
| Waktu | O(V + E) | Setiap vertex dan edge dikunjungi sekali |
| Ruang | O(V) | Stack + himpunan visited, kasus terburuk semua node |
| Penelusuran | Terdalam lebih dulu | Menyusuri satu cabang sampai ujung sebelum cabang lainnya |
| Struktur data | Stack (atau rekursi) | Urutan LIFO yang menghasilkan perilaku depth-first |
Langkah demi langkah
| Langkah | Apa yang terjadi |
|---|---|
| 1 | Push node sumber ke stack. |
| 2 | Pop sebuah node; jika sudah dikunjungi, lewati. |
| 3 | Tandai sudah dikunjungi dan catat edge yang mencapainya. |
| 4 | Push semua tetangganya yang belum dikunjungi ke stack. |
| 5 | Ulangi sampai stack kosong. |
Contoh penerapan
DFS iteratif dari A pada graf A → [B, C], B → [D], C → [E], dengan tetangga di-push sesuai urutan daftar (stack LIFO mengeluarkan yang terakhir di-push lebih dulu):
| Langkah | Stack (atas di kanan) | Sudah dikunjungi | Aksi |
|---|---|---|---|
| 1 | [A] | {} | Push sumber A. |
| 2 | [B, C] | {A} | Pop A, tandai sudah dikunjungi, push tetangga B lalu C. |
| 3 | [B, E] | {A, C} | Pop C, tandai sudah dikunjungi, push tetangga E. |
| 4 | [B] | {A, C, E} | Pop E, tandai sudah dikunjungi, tanpa tetangga. |
| 5 | [D] | {A, C, E, B} | Pop B, tandai sudah dikunjungi, push tetangga D. |
| 6 | [] | {A, C, E, B, D} | Pop D, tandai sudah dikunjungi, stack kosong: selesai. |
Kapan memakai DFS
| Pakai saat | Hindari saat |
|---|---|
| Kamu butuh deteksi siklus, topological sort, atau mencari komponen terhubung. | Kamu butuh jalur terpendek pada graf tak berbobot: BFS bisa melakukannya, DFS tidak. |
| Grafnya lebar dan dangkal, jadi stack memakai jauh lebih sedikit memori daripada queue berisi banyak node frontier. | Grafnya sangat dalam dan kamu memakai rekursi: call stack bisa overflow. |
| Kamu ingin menjelajahi semua jalur secara menyeluruh, seperti pada pemecahan labirin atau pencarian backtracking. | Kamu butuh node ditemukan sesuai urutan jaraknya dari sumber. |
| Kamu memeriksa keterjangkauan atau ada tidaknya jalur antara dua node. | Kamu harus menjamin jumlah edge paling sedikit pada jalur yang ditemukan. |
Kode Depth-First Search
Implementasi Depth-First Search yang rapi dan siap dijalankan dalam Python, JavaScript, Java, C++, C. Pilih bahasa, salin kodenya, atau buka langsung di Coddy Playground.
Kode Depth-First Search dalam Python
1def dfs(graph, node, visited):2 visited.add(node)3 order = [node]4 for neighbor in graph[node]:5 if neighbor not in visited:6 order.extend(dfs(graph, neighbor, visited))7 return order8
9
10graph = {11 "A": ["B", "C"],12 "B": ["D", "E"],13 "C": ["F"],14 "D": [],15 "E": ["F"],16 "F": [],17}18
19order = dfs(graph, "A", set())20print("DFS order:", " -> ".join(order))Kode Depth-First Search dalam JavaScript
1const graph = {2 A: ["B", "C"],3 B: ["D", "E"],4 C: ["F"],5 D: [],6 E: ["F"],7 F: [],8};9
10function dfs(node, visited = new Set(), order = []) {11 if (visited.has(node)) return order;12 visited.add(node);13 order.push(node);14 for (const next of graph[node]) {15 dfs(next, visited, order);16 }17 return order;18}19
20console.log("DFS from A:", dfs("A").join(" -> "));Kode Depth-First Search dalam Java
1import java.util.List;2
3public class Main {4 static List<List<Integer>> adj = List.of(5 List.of(1, 2), // neighbors of 06 List.of(0, 3, 4), // neighbors of 17 List.of(0, 5), // neighbors of 28 List.of(1),9 List.of(1, 5),10 List.of(2, 4)11 );12 static boolean[] visited = new boolean[6];13
14 static void dfs(int node) {15 visited[node] = true;16 System.out.print(" " + node);17 for (int next : adj.get(node)) {18 if (!visited[next]) dfs(next);19 }20 }21
22 public static void main(String[] args) {23 System.out.print("DFS from 0:");24 dfs(0);25 System.out.println();26 }27}Kode Depth-First Search dalam C++
1#include <iostream>2#include <vector>3
4void dfs(int node, const std::vector<std::vector<int>>& adj,5 std::vector<bool>& visited) {6 visited[node] = true;7 std::cout << node << " ";8 // Recurse into every unvisited neighbor9 for (int next : adj[node]) {10 if (!visited[next]) dfs(next, adj, visited);11 }12}13
14int main() {15 // 6-node undirected graph as an adjacency list16 std::vector<std::vector<int>> adj = {17 {1, 2}, // 018 {0, 3, 4}, // 119 {0, 5}, // 220 {1}, // 321 {1, 5}, // 422 {2, 4}, // 523 };24 std::vector<bool> visited(adj.size(), false);25 std::cout << "DFS from node 0: ";26 dfs(0, adj, visited);27 std::cout << "\n";28 return 0;29}Kode Depth-First Search dalam C
1#include <stdbool.h>2#include <stdio.h>3
4#define N 65
6// 6-node undirected graph as an adjacency matrix7int adj[N][N] = {8 {0, 1, 1, 0, 0, 0},9 {1, 0, 0, 1, 1, 0},10 {1, 0, 0, 0, 0, 1},11 {0, 1, 0, 0, 0, 0},12 {0, 1, 0, 0, 0, 1},13 {0, 0, 1, 0, 1, 0},14};15bool visited[N];16
17void dfs(int node) {18 visited[node] = true;19 printf("%d ", node);20 // Recurse into every unvisited neighbor21 for (int next = 0; next < N; next++) {22 if (adj[node][next] && !visited[next]) dfs(next);23 }24}25
26int main(void) {27 printf("DFS from node 0: ");28 dfs(0);29 printf("\n");30 return 0;31}FAQ Depth-First Search
Berapa kompleksitas waktu DFS?
O(V + E), dengan V adalah jumlah vertex dan E jumlah edge, karena DFS mengunjungi setiap vertex sekali dan memeriksa setiap edge sekali. DFS memakai ruang O(V) untuk stack dan himpunan visited.Apa perbedaan DFS dan BFS?
Apakah DFS rekursif atau iteratif?
Keduanya bisa. Versi rekursif memakai call stack program secara implisit dan sangat ringkas; versi iteratif memakai stack eksplisit (seperti animasi di sini) dan menghindari stack overflow akibat rekursi yang dalam pada graf besar.
Kapan sebaiknya memakai DFS, bukan BFS?
Pakai DFS saat kamu perlu menjelajahi jalur secara utuh: deteksi siklus, topological sort, komponen terhubung, atau pencarian backtracking seperti memecahkan labirin. DFS juga memakai lebih sedikit memori pada graf lebar, karena stack hanya menyimpan frontier dari satu cabang dalam satu waktu. Pakai BFS setiap kali kamu butuh jalur terpendek pada graf tak berbobot atau node sesuai urutan jaraknya dari sumber.
Bisakah DFS menemukan jalur terpendek?
Mengapa DFS butuh himpunan visited?
O(V + E). Kesalahan yang umum adalah menandai node hanya saat di-pop sambil tetap mem-push duplikat. Cara itu benar, tetapi bisa meninggalkan entri usang di stack yang harus dilewati.