0Pricing
Coding Interview Prep · Pelajaran

Representasi Graf dan Persiapan Traversal

Bangun graf berarah dan tak berarah dengan list ketetanggaan, inisialisasi BFS dengan deque, serta DFS dengan stack atau rekursi sambil melacak simpul yang telah dikunjungi.

Representasi Graf dan Persiapan Traversal adalah pelajaran Coding Interview Prep gratis di CoddyKit. Ini adalah pelajaran 1 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Coding Interview Prep, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Coding Interview Prep mencakup 4 pelajaran total.

Apa Itu Graf?

Graf adalah kumpulan simpul (verteks) yang dihubungkan oleh sisi. Tidak seperti pohon, graf dapat memiliki siklus, beberapa jalur antara simpul, dan komponen yang tidak terhubung. Graf memodelkan sistem dunia nyata seperti jejaring sosial, peta jalan, pohon dependensi, dan tautan halaman web. Hampir setiap wawancara desain sistem dan algoritma yang tidak sederhana membahas graf—menguasai representasi dan penelusurannya sangat penting.

# Graph terminology:
# - V: set of vertices (nodes)
# - E: set of edges
# - Directed graph: edges have direction (A -> B but not B -> A)
# - Undirected graph: edges are bidirectional
# - Weighted graph: edges have costs/weights
# - Cyclic: contains at least one cycle
# - Acyclic: no cycles (DAG = Directed Acyclic Graph)
# - Connected: every node reachable from every other
# - Disconnected: multiple isolated components
print('Graph: nodes + edges, directed/undirected, weighted/unweighted')

Representasi Daftar Ketetanggaan

Sebuah daftar ketetanggaan menyimpan daftar tetangga dari setiap simpul. Dalam Python, gunakan dict yang memetakan setiap simpul ke daftar simpul yang berdekatan. Ini adalah representasi yang paling umum dalam soal wawancara: ruang O(V + E) (efisien untuk graf renggang), O(derajat) untuk menelusuri tetangga, dan rata-rata O(1) untuk memeriksa ketetanggaan dengan varian himpunan pencincangan. Sebagian besar soal graf di LeetCode menggunakan format ini.

from collections import defaultdict

# Build an undirected graph
def build_undirected(edges):
    graph = defaultdict(list)
    for u, v in edges:
        graph[u].append(v)
        graph[v].append(u)  # both directions
    return graph

edges = [(0,1), (0,2), (1,3), (2,3), (3,4)]
graph = build_undirected(edges)
print(dict(graph))
# {0:[1,2], 1:[0,3], 2:[0,3], 3:[1,2,4], 4:[3]}

# Directed graph: only one direction
def build_directed(edges):
    graph = defaultdict(list)
    for u, v in edges:
        graph[u].append(v)  # only u -> v
    return graph

Representasi Matriks Ketetanggaan

Sebuah matriks ketetanggaan adalah larik 2D berukuran V×V, dengan matrix[i][j] = 1 (atau bobot sisi) jika terdapat sisi dari i ke j, dan 0 jika tidak. Representasi ini menyediakan pencarian sisi dalam O(1), tetapi menggunakan ruang O(V²) tanpa memedulikan jumlah sisi—boros untuk graf renggang. Representasi ini lebih disukai ketika grafnya padat (memiliki banyak sisi) atau ketika pemeriksaan keberadaan sisi yang cepat sangat penting, seperti dalam algoritma Floyd-Warshall untuk jalur terpendek semua pasangan.

# Adjacency matrix for 5 nodes
V = 5
matrix = [[0] * V for _ in range(V)]

edges = [(0,1), (0,2), (1,3), (2,3), (3,4)]
for u, v in edges:
    matrix[u][v] = 1
    matrix[v][u] = 1  # undirected

# Print the matrix:
for row in matrix:
    print(row)
# Neighbour check: O(1)
print('Edge 0-2:', bool(matrix[0][2]))  # True
print('Edge 0-4:', bool(matrix[0][4]))  # False

# Space: O(V^2) vs adjacency list O(V+E)
# Dense graph: matrix often better; sparse: list better

Representasi Daftar Sisi

Sebuah daftar sisi adalah representasi paling sederhana: cukup berupa daftar tupel (sumber, tujuan), yang dapat dilengkapi dengan bobot. Representasi ini menggunakan ruang O(E) dan memudahkan penelusuran semua sisi. Namun, untuk menemukan tetangga suatu simpul, Anda harus memindai semua sisi: O(E). Daftar sisi digunakan dalam algoritma graf yang menelusuri setiap sisi secara tepat, seperti Bellman-Ford (merelaksasi semua sisi sebanyak n-1 kali) dan algoritma pohon rentang minimum Kruskal.

# Weighted edge list: (source, destination, weight)
edge_list = [
    (0, 1, 4),
    (0, 2, 1),
    (1, 3, 1),
    (2, 3, 5),
    (3, 4, 3)
]

# Useful for:
# Bellman-Ford: iterate all edges n-1 times
# Kruskal's MST: sort by weight then union-find

# Sort by weight for Kruskal:
edge_list_sorted = sorted(edge_list, key=lambda e: e[2])
print('Sorted by weight:', edge_list_sorted)

# Finding neighbours: O(E) scan -- inefficient for traversal
node_0_neighbors = [v for u, v, w in edge_list if u == 0]
print('Node 0 neighbors:', node_0_neighbors)

Persiapan BFS: Antrean dan Himpunan yang Telah Dikunjungi

BFS (Penelusuran Melebar) menjelajahi graf level demi level menggunakan antrean. Komponen pentingnya adalah himpunan yang telah dikunjungi untuk mencegah simpul dikunjungi kembali dalam graf yang memiliki siklus. Tanpa himpunan ini, BFS pada graf bersiklus akan berulang selamanya. Persiapan standar: inisialisasi antrean dengan simpul sumber, tandai simpul tersebut sebagai telah dikunjungi, lalu berulang kali keluarkan simpul dari antrean, proses, dan masukkan tetangga yang belum dikunjungi ke antrean.

from collections import deque

def bfs(graph, start):
    visited = {start}        # mark source as visited
    queue = deque([start])   # initialise queue
    order = []
    while queue:
        node = queue.popleft()
        order.append(node)
        for neighbour in graph[node]:
            if neighbour not in visited:
                visited.add(neighbour)     # mark BEFORE enqueue
                queue.append(neighbour)
    return order

from collections import defaultdict
graph = defaultdict(list)
for u, v in [(0,1),(0,2),(1,3),(2,3),(3,4)]:
    graph[u].append(v); graph[v].append(u)

print(bfs(graph, 0))  # [0, 1, 2, 3, 4]

Persiapan DFS: Tumpukan atau Rekursi

DFS (Penelusuran Mendalam) menjelajahi setiap cabang sejauh mungkin sebelum melakukan penelusuran mundur. Implementasikan secara rekursif (menggunakan tumpukan pemanggilan) atau secara iteratif (menggunakan tumpukan eksplisit). Keduanya memerlukan himpunan yang telah dikunjungi untuk graf yang memiliki siklus. Versi iteratif memasukkan tetangga dalam urutan terbalik agar sesuai dengan urutan penelusuran DFS rekursif, meskipun urutan penjelajahan dapat berbeda antara kedua implementasi tersebut.

def dfs_recursive(graph, node, visited=None, order=None):
    if visited is None: visited = set(); order = []
    visited.add(node)
    order.append(node)
    for neighbour in graph[node]:
        if neighbour not in visited:
            dfs_recursive(graph, neighbour, visited, order)
    return order

def dfs_iterative(graph, start):
    visited = set()
    stack = [start]
    order = []
    while stack:
        node = stack.pop()
        if node in visited: continue
        visited.add(node)
        order.append(node)
        for neighbour in reversed(graph[node]):  # reverse for same order as recursive
            if neighbour not in visited:
                stack.append(neighbour)
    return order

print('Recursive DFS:', dfs_recursive(graph, 0))
print('Iterative DFS:', dfs_iterative(graph, 0))

Kapan Menggunakan BFS atau DFS

Pilih BFS ketika Anda memerlukan jalur terpendek (jumlah sisi paling sedikit) dalam graf tak berbobot, atau ketika Anda perlu memproses simpul level demi level. Pilih DFS ketika Anda perlu menjelajahi semua simpul yang dapat dicapai, mendeteksi siklus, menemukan komponen terhubung, melakukan pengurutan topologis, atau mencantumkan semua jalur. Dalam praktiknya: BFS untuk “jalur terpendek/lompatan minimum”, DFS untuk “keberadaan/keterjangkauan/pencacahan”.

# BFS use cases:
# - Shortest path in unweighted graph (fewest edges)
# - Level-order traversal
# - Word ladder (minimum transformations)
# - Clone graph

# DFS use cases:
# - Connected components (flood fill)
# - Cycle detection
# - Topological sort
# - All paths between two nodes
# - Maze solving (any path)
# - N-queens, Sudoku (backtracking)

# Both: O(V + E) time, O(V) space for visited
print('BFS: shortest hops | DFS: existence and enumeration')

Graf dari Format Input LeetCode

Soal graf LeetCode menggunakan berbagai format input. Daftar sisi: [[0,1],[0,2]]—buat daftar ketetanggaan. Daftar ketetanggaan berbasis indeks: graph[i] adalah daftar tetangga i. Kisi/matriks: larik 2D berukuran m×n, dengan sel sebagai simpul dan sel yang berdekatan (atas/bawah/kiri/kanan) sebagai tetangga. Node dengan anak: kelas khusus seperti Node(val, neighbors). Kenali format-format ini dan ubah menjadi daftar ketetanggaan sebagai langkah pertama Anda.

# Format 1: edge list -> adjacency list
def edges_to_adj(n, edges):
    graph = [[] for _ in range(n)]
    for u, v in edges:
        graph[u].append(v)
        graph[v].append(u)
    return graph

# Format 2: 2D grid -> adjacency (implicit)
# Neighbours of (r, c): (r-1,c), (r+1,c), (r,c-1), (r,c+1)
DIRS = [(-1,0),(1,0),(0,-1),(0,1)]
def grid_neighbours(grid, r, c):
    rows, cols = len(grid), len(grid[0])
    return [(r+dr, c+dc) for dr, dc in DIRS
            if 0 <= r+dr < rows and 0 <= c+dc < cols]

grid = [[1,1,0],[0,1,1],[1,0,0]]
print('Neighbours of (0,0):', grid_neighbours(grid, 0, 0))
print('Neighbours of (1,1):', grid_neighbours(grid, 1, 1))

Menandai Sel yang Telah Dikunjungi pada Kisi

Untuk soal kisi, ada dua cara melacak sel yang telah dikunjungi. Opsi A: gunakan himpunan visited terpisah yang berisi tupel (row, col)—ruang tambahan O(m*n). Opsi B: ubah kisi secara langsung dengan menandai sel yang telah dikunjungi menggunakan nilai penanda (misalnya, '#' atau 2), lalu pulihkan setelahnya jika diperlukan. Pendekatan langsung ini menggunakan ruang tambahan O(1) dan umum digunakan dalam masalah pengisian area serta penghitungan pulau.

def num_islands(grid):
    if not grid:
        return 0
    rows, cols = len(grid), len(grid[0])
    count = 0

    def dfs(r, c):
        if r < 0 or r >= rows or c < 0 or c >= cols:
            return
        if grid[r][c] != '1':
            return
        grid[r][c] = '#'  # mark as visited (in-place)
        dfs(r+1, c); dfs(r-1, c)
        dfs(r, c+1); dfs(r, c-1)

    for r in range(rows):
        for c in range(cols):
            if grid[r][c] == '1':
                dfs(r, c)
                count += 1
    return count

grid = [['1','1','0','0'],
        ['1','1','0','0'],
        ['0','0','1','0'],
        ['0','0','0','1']]
print(num_islands(grid))  # 3

Menginisialisasi BFS dengan Beberapa Sumber

BFS multi-sumber dimulai dari beberapa simpul secara bersamaan dengan menginisialisasi antrean menggunakan semua simpul sumber yang telah ditandai sebagai telah dikunjungi. Pendekatan ini digunakan dalam soal seperti “jarak dari 0 terdekat”, “jeruk yang membusuk”, dan “dinding dan gerbang”, ketika Anda menginginkan jarak terpendek dari salah satu simpul sumber. BFS multi-sumber berjalan dalam O(V + E)—sama seperti BFS dengan satu sumber—karena setiap simpul tetap dikunjungi paling banyak satu kali.

from collections import deque

def rotting_oranges(grid):
    rows, cols = len(grid), len(grid[0])
    queue = deque()
    fresh = 0
    # Multi-source: all rotten oranges start at time=0
    for r in range(rows):
        for c in range(cols):
            if grid[r][c] == 2:
                queue.append((r, c, 0))  # (row, col, time)
            elif grid[r][c] == 1:
                fresh += 1
    dirs = [(0,1),(0,-1),(1,0),(-1,0)]
    time = 0
    while queue:
        r, c, t = queue.popleft()
        for dr, dc in dirs:
            nr, nc = r+dr, c+dc
            if 0<=nr<rows and 0<=nc<cols and grid[nr][nc]==1:
                grid[nr][nc] = 2  # mark rotten
                fresh -= 1
                queue.append((nr, nc, t+1))
                time = t + 1
    return time if fresh == 0 else -1

print(rotting_oranges([[2,1,1],[1,1,0],[0,1,1]]))  # 4

Kepadatan Graf dan Pemilihan Representasi

Pilihan antara daftar ketetanggaan dan matriks bergantung pada kepadatan graf—rasio E/V². Graf renggang (E << V²) mendapatkan manfaat dari daftar ketetanggaan: ruang O(V+E) dibandingkan O(V²) untuk matriks. Graf padat (E ≈ V²) mendapatkan manfaat dari matriks ketetanggaan: pencarian sisi O(1) dibandingkan O(derajat) untuk daftar. Dalam soal wawancara, daftar ketetanggaan hampir selalu merupakan pilihan yang tepat karena sebagian besar soal melibatkan graf renggang.

# Graph density comparison:
# Sparse: social network (V=1B users, avg 200 friends)
#   E = 200 * 1B = 200B << V^2 = 10^18 -> adjacency list
# Dense: complete graph (every node connected to every other)
#   E = V*(V-1)/2 ≈ V^2 -> adjacency matrix

# Interview rule of thumb:
# - Default to adjacency list (defaultdict(list))
# - Use matrix only when asked about dense graph or O(1) edge lookup
# - Grid problems: use implicit adjacency (4-directional neighbours)

print('Sparse graph (E << V^2): use adjacency list')
print('Dense graph (E ~ V^2): consider adjacency matrix')

Pemeriksaan Singkat

Uji pemahaman Anda tentang konsep Struktur Data & Algoritma—Persiapan Wawancara Pemrograman dari pelajaran ini.

Ringkasan Pelajaran

Dalam pelajaran ini Anda mempelajari: tiga representasi graf (daftar ketetanggaan, matriks, daftar sisi) dan kapan memilih masing-masing, persiapan BFS dan DFS dengan himpunan yang telah dikunjungi untuk mencegah perulangan tak terbatas pada graf yang memiliki siklus, serta pola praktis seperti penandaan kisi secara langsung dan BFS multi-sumber. Selanjutnya, kita menerapkan BFS untuk menemukan jalur terpendek dan melakukan penelusuran per level.

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Representasi Graf dan Persiapan Traversal” gratis?

Ya — teks lengkap “Representasi Graf dan Persiapan Traversal” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Coding Interview Prep, upgrade ke CoddyKit PRO. Kursus Coding Interview Prep mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Representasi Graf dan Persiapan Traversal”?

Bangun graf berarah dan tak berarah dengan list ketetanggaan, inisialisasi BFS dengan deque, serta DFS dengan stack atau rekursi sambil melacak simpul yang telah dikunjungi. Kamu berlatih Coding Interview Prep dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai Coding Interview Prep?

Tidak diperlukan pengalaman sebelumnya. Coding Interview Prep di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 1 dari 4.

Berapa lama pelajaran “Representasi Graf dan Persiapan Traversal” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran Coding Interview Prep ini?

Ya. Setiap pelajaran Coding Interview Prep menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Representasi Graf dan Persiapan Traversal
  2. BFS: Jalur Terpendek dan Traversal Level
  3. DFS: Komponen Terhubung dan Flood Fill
  4. Deteksi Siklus pada Graf Berarah dan Tak Berarah
← Kembali ke Coding Interview Prep