BFS: Jalur Terpendek dan Traversal Level
Gunakan BFS untuk menemukan jalur terpendek dalam graf tak berbobot, selesaikan word-ladder per level, dan kloning graf menggunakan hash map.
BFS: Jalur Terpendek dan Traversal Level adalah pelajaran Coding Interview Prep gratis di CoddyKit. Ini adalah pelajaran 2 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.
BFS dan Jalur Terpendek pada Graf Tak Berbobot
BFS menemukan jalur terpendek (jumlah sisi paling sedikit) dalam graf tak berbobot karena menjelajahi simpul berdasarkan jarak yang meningkat dari sumber. Saat sebuah simpul pertama kali dicapai selama BFS, simpul tersebut dicapai melalui jalur yang paling pendek. Sifat ini tidak berlaku untuk DFS. Untuk graf berbobot dengan bobot non-negatif, gunakan algoritma Dijkstra—BFS secara implisit menganggap semua sisi memiliki bobot 1.
from collections import deque, defaultdict
def shortest_path(graph, start, end):
if start == end:
return 0
visited = {start}
queue = deque([(start, 0)]) # (node, distance)
while queue:
node, dist = queue.popleft()
for neighbour in graph[node]:
if neighbour == end:
return dist + 1
if neighbour not in visited:
visited.add(neighbour)
queue.append((neighbour, dist + 1))
return -1 # no path found
graph = defaultdict(list)
for u, v in [(0,1),(1,2),(2,3),(0,3),(1,4)]:
graph[u].append(v); graph[v].append(u)
print(shortest_path(graph, 0, 3)) # 1 (direct edge)
print(shortest_path(graph, 0, 4)) # 2 (0->1->4)Melacak Jalur Terpendek yang Sebenarnya
Untuk merekonstruksi jalur yang sebenarnya (bukan hanya panjangnya), pertahankan kamus induk yang mencatat cara setiap simpul dicapai. Saat mencapai tujuan, telusuri kembali pemetaan induk dari akhir ke awal, lalu balik hasilnya. Cara ini menambahkan ruang O(V) untuk pemetaan induk, tetapi menyediakan jalur lengkap dalam waktu O(panjang_jalur) setelah BFS selesai.
from collections import deque, defaultdict
def shortest_path_with_route(graph, start, end):
parent = {start: None}
queue = deque([start])
while queue:
node = queue.popleft()
if node == end:
break
for nb in graph[node]:
if nb not in parent:
parent[nb] = node
queue.append(nb)
if end not in parent:
return [] # no path
# Reconstruct path by tracing back
path = []
node = end
while node is not None:
path.append(node)
node = parent[node]
return path[::-1] # reverse
graph = defaultdict(list)
for u, v in [(0,1),(1,2),(2,3),(0,4),(4,3)]:
graph[u].append(v); graph[v].append(u)
print(shortest_path_with_route(graph, 0, 3)) # [0, 4, 3] or [0, 1, 2, 3]Tangga Kata: BFS pada Graf Implisit
Tangga Kata (LeetCode #127) meminta jumlah minimum perubahan satu karakter untuk mengubah kata awal menjadi kata akhir, dengan syarat setiap kata perantara harus ada dalam kamus. Ini adalah BFS pada graf implisit, dengan simpul berupa kata dan sisi yang menghubungkan kata-kata yang berbeda satu huruf. Hasilkan semua mutasi satu huruf dan periksa apakah mutasi tersebut ada dalam himpunan kata. BFS menjamin urutan transformasi minimum.
from collections import deque
def word_ladder(begin_word, end_word, word_list):
word_set = set(word_list)
if end_word not in word_set:
return 0
queue = deque([(begin_word, 1)])
visited = {begin_word}
while queue:
word, steps = queue.popleft()
for i in range(len(word)):
for c in 'abcdefghijklmnopqrstuvwxyz':
new_word = word[:i] + c + word[i+1:]
if new_word == end_word:
return steps + 1
if new_word in word_set and new_word not in visited:
visited.add(new_word)
queue.append((new_word, steps + 1))
return 0
print(word_ladder('hit', 'cog', ['hot','dot','dog','lot','log','cog'])) # 5Penelusuran per Level: Melacak Jarak
Penelusuran per level mengelompokkan simpul berdasarkan jaraknya dari sumber, yang sangat berguna untuk soal yang memerlukan pemrosesan per level. Lacak jarak dengan menyimpannya dalam elemen antrean sebagai tupel (node, dist), atau gunakan teknik ukuran antrean (catat ukuran antrean sebelum setiap level, proses tepat sejumlah simpul tersebut, lalu tambah penghitung level). Kedua pendekatan menghasilkan hasil yang sama.
from collections import deque, defaultdict
def bfs_levels(graph, start):
levels = {}
visited = {start}
queue = deque([start])
dist = 0
while queue:
# Process all nodes at current distance
for _ in range(len(queue)):
node = queue.popleft()
levels[node] = dist
for nb in graph[node]:
if nb not in visited:
visited.add(nb)
queue.append(nb)
dist += 1
return levels
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_levels(graph, 0)) # {0:0, 1:1, 2:1, 3:2, 4:3}Klon Graf
Klon Graf (LeetCode #133) membuat salinan mendalam dari graf tak berarah yang terhubung. Gunakan BFS dan peta pencincangan yang memetakan simpul asli ke klonnya. Saat pertama kali mengunjungi sebuah simpul, buat klonnya dan tambahkan ke peta. Saat memproses tetangga, cari atau buat klon mereka, lalu hubungkan sisi-sisinya. Peta pencincangan memiliki dua fungsi: melacak simpul yang telah dikunjungi dan memetakan simpul asli ke salinannya.
from collections import deque
class Node:
def __init__(self, val=0, neighbors=None):
self.val = val
self.neighbors = neighbors if neighbors is not None else []
def clone_graph(node):
if not node:
return None
old_to_new = {node: Node(node.val)}
queue = deque([node])
while queue:
curr = queue.popleft()
for nb in curr.neighbors:
if nb not in old_to_new:
old_to_new[nb] = Node(nb.val)
queue.append(nb)
old_to_new[curr].neighbors.append(old_to_new[nb])
return old_to_new[node]
# Build a simple graph: 1 -- 2 -- 3 -- 4 -- 1
n1 = Node(1); n2 = Node(2); n3 = Node(3); n4 = Node(4)
n1.neighbors = [n2, n4]; n2.neighbors = [n1, n3]
n3.neighbors = [n2, n4]; n4.neighbors = [n3, n1]
cloned = clone_graph(n1)
print(cloned.val, [n.val for n in cloned.neighbors]) # 1 [2, 4]BFS Dua Arah
BFS dua arah memulai BFS dari sumber dan tujuan secara bersamaan, dengan memperluas satu level setiap kali dari kedua ujung. Saat kedua batas pencarian bertemu, Anda telah menemukan jalur terpendek. Untuk graf besar, pendekatan ini mengurangi ruang pencarian dari O(b^d) menjadi O(2 * b^(d/2)), dengan b sebagai faktor percabangan dan d sebagai panjang jalur—peningkatan yang sangat besar untuk graf dengan konektivitas tinggi seperti Tangga Kata dengan kamus besar.
from collections import defaultdict
def word_ladder_bidir(begin, end, word_list):
word_set = set(word_list)
if end not in word_set:
return 0
front, back = {begin}, {end}
visited = {begin, end}
steps = 1
while front and back:
# Always expand the smaller frontier
if len(front) > len(back):
front, back = back, front
next_front = set()
for word in front:
for i in range(len(word)):
for c in 'abcdefghijklmnopqrstuvwxyz':
nw = word[:i] + c + word[i+1:]
if nw in back: # frontiers met!
return steps + 1
if nw in word_set and nw not in visited:
visited.add(nw)
next_front.add(nw)
front = next_front
steps += 1
return 0
print(word_ladder_bidir('hit','cog',['hot','dot','dog','lot','log','cog'])) # 5BFS 0-1 untuk Graf Berbobot
BFS 0-1 menangani graf yang bobot sisinya hanya 0 atau 1. Alih-alih menggunakan antrean biasa, gunakan antrean berujung ganda: gunakan append di bagian belakang untuk sisi berbobot 1 (level berikutnya) dan di bagian depan untuk sisi berbobot 0 (level yang sama). Cara ini menghasilkan perhitungan jalur terpendek dalam O(V + E)—lebih cepat daripada O((V+E) log V) milik Dijkstra ketika bobotnya biner. Pendekatan ini umum digunakan dalam soal kisi ketika beberapa perpindahan gratis dan perpindahan lainnya berbiaya 1.
from collections import deque
def zero_one_bfs(graph, start, n):
# graph: list of (neighbour, weight) where weight is 0 or 1
dist = [float('inf')] * n
dist[start] = 0
dq = deque([start])
while dq:
node = dq.popleft()
for nb, w in graph[node]:
if dist[node] + w < dist[nb]:
dist[nb] = dist[node] + w
if w == 0:
dq.appendleft(nb) # same level
else:
dq.append(nb) # next level
return dist
# Simple test:
graph = [[(1, 0), (2, 1)], # node 0: free to 1, cost 1 to 2
[(3, 1)], # node 1: cost 1 to 3
[(3, 0)], # node 2: free to 3
[]]
print(zero_one_bfs(graph, 0, 4)) # [0, 0, 1, 1]Dinding dan Gerbang (BFS Multi-Sumber)
Dinding dan Gerbang mengisi setiap ruangan kosong dengan jarak ke gerbang terdekatnya. Gunakan BFS multi-sumber: inisialisasi antrean secara bersamaan dengan semua gerbang (bernilai 0), lalu perluas pencarian ke arah luar. Nilai setiap sel ditetapkan berdasarkan level saat sel tersebut pertama kali dicapai. Solusi O(mn) ini lebih efisien daripada menjalankan BFS secara terpisah dari setiap ruangan kosong, yang akan membutuhkan O(m²n²).
from collections import deque
def walls_and_gates(rooms):
if not rooms:
return
rows, cols = len(rooms), len(rooms[0])
INF = float('inf')
queue = deque()
# Multi-source: all gates at distance 0
for r in range(rows):
for c in range(cols):
if rooms[r][c] == 0: # gate
queue.append((r, c))
dirs = [(0,1),(0,-1),(1,0),(-1,0)]
while queue:
r, c = queue.popleft()
for dr, dc in dirs:
nr, nc = r+dr, c+dc
if 0<=nr<rows and 0<=nc<cols and rooms[nr][nc]==INF:
rooms[nr][nc] = rooms[r][c] + 1
queue.append((nr, nc))
rooms = [[float('inf'),-1,0,float('inf')],
[float('inf'),float('inf'),float('inf'),-1],
[float('inf'),-1,float('inf'),-1],
[0,-1,float('inf'),float('inf')]]
walls_and_gates(rooms)
print(rooms[0][0], rooms[1][1]) # 3, 2BFS Ular dan Tangga
Ular dan Tangga (LeetCode #909) adalah soal jalur terpendek dengan BFS pada kisi bernomor. Modelkan papan sebagai graf tak berbobot, dengan kemungkinan bergerak 1–6 langkah dari setiap kotak dan mendarat pada ular atau tangga yang memindahkan Anda secara teleportasi. BFS menemukan jumlah lemparan dadu minimum. Tantangan utamanya adalah mengonversi posisi 1D menjadi koordinat papan 2D, dengan memperhitungkan tata letak yang arah barisnya bergantian.
from collections import deque
def snakes_and_ladders(board):
n = len(board)
def get_board(pos):
r, c = divmod(pos - 1, n)
if r % 2 == 1: c = n - 1 - c # alternating direction
return board[n - 1 - r][c]
visited = {1}
queue = deque([(1, 0)])
while queue:
pos, moves = queue.popleft()
for dice in range(1, 7):
next_pos = pos + dice
if next_pos > n * n:
break
val = get_board(next_pos)
if val != -1:
next_pos = val # snake or ladder
if next_pos == n * n:
return moves + 1
if next_pos not in visited:
visited.add(next_pos)
queue.append((next_pos, moves + 1))
return -1
print('BFS models game as an unweighted shortest-path problem')Kompleksitas dan Optimasi BFS
Kompleksitas waktu BFS adalah O(V + E) karena setiap simpul dimasukkan ke antrean satu kali dan setiap sisi diperiksa sejumlah konstan kali. Kompleksitas ruangnya adalah O(V) untuk himpunan yang telah dikunjungi dan antrean. Untuk graf kisi, V = m*n dan E = 4*m*n (setiap sel memiliki 4 tetangga), sehingga BFS pada kisi adalah O(mn). Optimasi utama: gunakan himpunan untuk pencarian O(1), bukan daftar untuk pencarian O(n). Tandai sel sebagai telah dikunjungi saat memasukkannya ke antrean, bukan saat mengeluarkannya.
# BFS on a graph with V vertices and E edges:
# Time: O(V + E) -- each vertex and edge visited once
# Space: O(V) -- visited set + queue
# BFS on an m x n grid:
# V = m*n cells
# E <= 4*m*n edges (4 directions, max)
# Time: O(m*n)
# Space: O(m*n)
# Common pitfalls:
# 1. Marking visited on dequeue (not enqueue) -> same node queued multiple times
# 2. Using a list for visited -> O(n) membership check -> O(V*E) total
# 3. Not handling disconnected graph -> BFS from single source misses components
print('O(V+E) time, O(V) space -- mark visited on enqueue')Nol Terdekat dalam Matriks Biner
Matriks 01 (LeetCode #542) menemukan jarak dari setiap sel ke 0 terdekat. BFS multi-sumber dari semua 0 secara bersamaan memberikan solusi optimal O(mn). Inisialisasi antrean dengan semua sel 0 pada jarak 0 dan semua sel 1 pada jarak tak terhingga. BFS menyebarkan jarak ke luar dari sel-sel 0, dengan menetapkan jarak setiap sel 1 saat pertama kali dicapai (yang dijamin merupakan jarak terpendek).
from collections import deque
def update_matrix(mat):
rows, cols = len(mat), len(mat[0])
dist = [[float('inf')] * cols for _ in range(rows)]
queue = deque()
for r in range(rows):
for c in range(cols):
if mat[r][c] == 0:
dist[r][c] = 0
queue.append((r, c))
dirs = [(0,1),(0,-1),(1,0),(-1,0)]
while queue:
r, c = queue.popleft()
for dr, dc in dirs:
nr, nc = r+dr, c+dc
if 0<=nr<rows and 0<=nc<cols:
if dist[r][c] + 1 < dist[nr][nc]:
dist[nr][nc] = dist[r][c] + 1
queue.append((nr, nc))
return dist
mat = [[0,0,0],[0,1,0],[1,1,1]]
result = update_matrix(mat)
for row in result: print(row) # [[0,0,0],[0,1,0],[1,2,1]]Pemeriksaan Singkat
Uji pemahaman Anda tentang konsep Struktur Data & Algoritma—Persiapan Wawancara Pemrograman dari pelajaran ini.
Ringkasan Pelajaran
Dalam pelajaran ini Anda mempelajari: BFS untuk jalur terpendek dalam graf tak berbobot dengan pelacakan induk untuk merekonstruksi rute, Tangga Kata sebagai contoh BFS kanonis pada graf implisit, BFS dua arah untuk graf besar, dan BFS multi-sumber untuk soal dengan beberapa titik awal. Selanjutnya, kita menerapkan DFS pada komponen terhubung dan pengisian area.
Pertanyaan yang Sering Diajukan
Apakah pelajaran “BFS: Jalur Terpendek dan Traversal Level” gratis?
Ya — teks lengkap “BFS: Jalur Terpendek dan Traversal Level” 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 “BFS: Jalur Terpendek dan Traversal Level”?
Gunakan BFS untuk menemukan jalur terpendek dalam graf tak berbobot, selesaikan word-ladder per level, dan kloning graf menggunakan hash map. 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 2 dari 4.
Berapa lama pelajaran “BFS: Jalur Terpendek dan Traversal Level” 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
- Representasi Graf dan Persiapan Traversal
- BFS: Jalur Terpendek dan Traversal Level
- DFS: Komponen Terhubung dan Flood Fill
- Deteksi Siklus pada Graf Berarah dan Tak Berarah