Perwakilan Graf dan Persediaan Lintasan
Bina graf berarah dan tidak berarah dengan senarai kejiranan, mulakan BFS dengan deque dan DFS dengan tindanan atau rekursi, sambil menjejak nod yang telah dilawati.
Perwakilan Graf dan Persediaan Lintasan ialah pelajaran Persediaan Temu Duga Pengaturcaraan percuma di CoddyKit. Ini ialah pelajaran 1 daripada 4. Anda boleh membaca keseluruhan pelajaran di bawah secara percuma — kemudian berlatih secara praktikal dalam pelayar menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7. Pelajaran ini merupakan sebahagian daripada laluan pembelajaran Persediaan Temu Duga Pengaturcaraan, dan kemajuan anda disegerakkan merentas web serta aplikasi CoddyKit. Kursus Persediaan Temu Duga Pengaturcaraan merangkumi sejumlah 4 pelajaran.
Apakah Graf?
Graf ialah himpunan nod (bucu) yang dihubungkan oleh sisi. Tidak seperti pepohon, graf boleh mempunyai kitaran, berbilang laluan antara nod dan komponen yang tidak bersambung. Graf memodelkan sistem dunia sebenar seperti rangkaian sosial, peta jalan, pepohon kebergantungan dan pautan halaman web. Hampir setiap temu duga reka bentuk sistem dan algoritma yang bukan remeh menyentuh graf — penguasaan terhadap perwakilan dan rentasannya adalah 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')Perwakilan Senarai Ketetanggaan
Satu senarai ketetanggaan menyimpan senarai jiran bagi setiap nod. Dalam Python, gunakan dict yang memetakan setiap nod kepada senarai nod bersebelahan. Ini ialah perwakilan yang paling lazim dalam soalan temu duga: ruang O(V + E) (cekap untuk graf jarang), O(darjah) untuk mengiterasi jiran, dan purata O(1) untuk memeriksa ketetanggaan dengan varian himpunan cincangan. Kebanyakan soalan graf 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 graphPerwakilan Matriks Ketetanggaan
Satu matriks ketetanggaan ialah tatasusunan 2D V×V yang mana matrix[i][j] = 1 (atau pemberat sisi) jika terdapat sisi dari i ke j, dan 0 jika sebaliknya. Ia menawarkan carian sisi O(1), tetapi menggunakan ruang O(V²) tanpa mengira bilangan sisi — membazir untuk graf jarang. Ia lebih sesuai apabila graf itu padat (mempunyai banyak sisi) atau apabila pemeriksaan kewujudan sisi yang pantas amat penting, seperti dalam laluan terpendek semua pasangan Floyd-Warshall.
# 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 betterPerwakilan Senarai Sisi
Satu senarai sisi ialah perwakilan yang paling ringkas: hanya senarai tupel (sumber, destinasi), dengan pemberat jika diperlukan. Ia menggunakan ruang O(E) dan mudah diiterasi untuk semua sisi. Walau bagaimanapun, untuk mencari jiran bagi sesuatu nod, semua sisi perlu diimbas: O(E). Senarai sisi digunakan dalam algoritma graf yang mengiterasi semua sisi dengan tepat, seperti Bellman-Ford (melonggarkan semua sisi sebanyak n-1 kali) dan algoritma pepohon rentangan 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)Persediaan BFS: Baris Gilir dan Himpunan Lawatan
BFS (Carian Mengikut Lebar) meneroka graf mengikut aras menggunakan baris gilir. Komponen penting ialah himpunan nod yang telah dilawati untuk mengelakkan nod dalam graf berkitar daripada dilawati semula. Tanpa himpunan ini, BFS pada graf berkitar akan berulang tanpa penghujung. Persediaan standardnya ialah: isikan baris gilir dengan nod sumber, tandainya sebagai telah dilawati, kemudian keluarkan nod daripada baris gilir, proses nod tersebut dan masukkan jiran yang belum dilawati ke dalam baris gilir secara berulang.
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]Persediaan DFS: Tindanan atau Rekursi
DFS (Carian Mendalam) meneroka sejauh yang mungkin sepanjang setiap cabang sebelum mengundur. Laksanakan secara rekursif (menggunakan tindanan panggilan) atau secara lelaran (menggunakan tindanan eksplisit). Kedua-duanya memerlukan himpunan nod yang telah dilawati untuk graf berkitar. Versi lelaran menolak jiran dalam susunan terbalik supaya sepadan dengan susunan perayauan DFS rekursif, walaupun susunan penerokaan mungkin berbeza antara kedua-dua pelaksanaan.
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))Bila Menggunakan BFS berbanding DFS
Pilih BFS apabila anda memerlukan laluan terpendek (bilangan sisi paling sedikit) dalam graf tidak berwajaran, atau apabila anda perlu memproses nod mengikut aras. Pilih DFS apabila anda perlu meneroka semua nod yang boleh dicapai, mengesan kitaran, mencari komponen bersambung, melakukan pengisihan topologi atau menyenaraikan semua laluan. Secara praktikal: BFS untuk “laluan terpendek/lompatan minimum”, DFS untuk “kewujudan/kebolehcapaian/penyenaraian”.
# 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 daripada Format Masukan LeetCode
Soalan graf LeetCode hadir dalam pelbagai format masukan. Senarai sisi: [[0,1],[0,2]] — bina senarai ketetanggaan. Senarai ketetanggaan berasaskan indeks: graph[i] ialah senarai jiran bagi i. Petak/matriks: tatasusunan 2D m×n yang selnya ialah nod dan sel bersebelahan (atas/bawah/kiri/kanan) ialah jiran. Node dengan anak: kelas tersuai seperti Node(val, neighbors). Kenal pasti format-format ini dan tukarkannya kepada senarai 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))Menanda Sel yang Telah Dilawati dalam Susunan Petak
Untuk soalan yang melibatkan susunan petak, terdapat dua cara untuk menjejak sel yang telah dilawati. Pilihan A: gunakan himpunan visited berasingan yang mengandungi tupel (row, col) — ruang tambahan O(m*n). Pilihan B: ubah suai susunan petak terus dengan menandakan sel yang telah dilawati menggunakan nilai penanda (contohnya, '#' atau 2) dan memulihkannya selepas itu jika diperlukan. Pendekatan terus menggunakan ruang tambahan O(1) dan lazim digunakan dalam masalah pengisian kawasan dan pengiraan 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)) # 3Memulakan BFS dengan Berbilang Sumber
BFS berbilang sumber bermula daripada beberapa nod secara serentak dengan mengisikan baris gilir menggunakan semua nod sumber yang telah ditandakan sebagai dilawati. Kaedah ini digunakan dalam masalah seperti “jarak dari sifar terdekat”, “oren mereput” dan “dinding dan pintu”, apabila anda mahukan jarak terpendek daripada mana-mana nod sumber. BFS berbilang sumber berjalan dalam O(V + E) — sama seperti BFS sumber tunggal — kerana setiap nod masih dilawati paling banyak sekali.
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]])) # 4Ketumpatan Graf dan Pemilihan Perwakilan
Pilihan antara senarai ketetanggaan dengan matriks bergantung pada ketumpatan graf — nisbah E/V². Graf jarang mendapat manfaat daripada senarai ketetanggaan: ruang O(V+E) berbanding O(V²) untuk matriks. Graf padat mendapat manfaat daripada matriks ketetanggaan: carian sisi O(1) berbanding O(darjah) untuk senarai. Untuk soalan temu duga, senarai ketetanggaan hampir selalu merupakan pilihan yang tepat kerana kebanyakan soalan melibatkan graf jarang.
# 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')Semakan Pantas
Uji pemahaman anda tentang konsep Struktur Data & Algoritma — Persediaan Temu Duga Pengekodan daripada pelajaran ini.
Rumusan Pelajaran
Dalam pelajaran ini, anda mempelajari: tiga perwakilan graf (senarai ketetanggaan, matriks dan senarai sisi) serta masa untuk memilih setiap satunya, persediaan BFS dan DFS dengan himpunan nod yang telah dilawati untuk mengelakkan gelung tanpa penghujung pada graf berkitar, dan corak praktikal seperti penandaan petak terus serta BFS berbilang sumber. Seterusnya, kita akan menggunakan BFS untuk mencari laluan terpendek dan melakukan perayauan mengikut aras.
Pelajari Persediaan Temu Duga Pengaturcaraan dengan tutor kecerdasan buatan — percuma
Tulis dan jalankan kod sebenar dalam pelayar anda, dapatkan bantuan segera daripada tutor kecerdasan buatan yang tersedia 24/7, dan sambung semula dari tempat anda berhenti di web atau dalam aplikasi.
- Kursus
- 90
- Pelajaran
- 360
Soalan Lazim
Adakah pelajaran “Perwakilan Graf dan Persediaan Lintasan” percuma?
Ya — teks penuh “Perwakilan Graf dan Persediaan Lintasan” boleh dibaca secara percuma di web ini. Untuk berlatih secara interaktif menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7, serta membuka kunci baki kursus Persediaan Temu Duga Pengaturcaraan, tingkat taraf kepada CoddyKit PRO. Kursus Persediaan Temu Duga Pengaturcaraan merangkumi sejumlah 4 pelajaran.
Apakah yang akan saya pelajari dalam “Perwakilan Graf dan Persediaan Lintasan”?
Bina graf berarah dan tidak berarah dengan senarai kejiranan, mulakan BFS dengan deque dan DFS dengan tindanan atau rekursi, sambil menjejak nod yang telah dilawati. Anda berlatih Persediaan Temu Duga Pengaturcaraan menggunakan kod praktikal yang dijalankan terus dalam pelayar, manakala tutor kecerdasan buatan 24/7 menjawab soalan anda semasa anda mengikuti pelajaran.
Adakah saya memerlukan pengalaman untuk memulakan Persediaan Temu Duga Pengaturcaraan?
Tiada pengalaman terdahulu diperlukan. Pembelajaran Persediaan Temu Duga Pengaturcaraan di CoddyKit disusun untuk pelajar daripada peringkat pemula hingga lanjutan, jadi anda boleh bermula di sini atau dari awal dan belajar mengikut kadar anda sendiri. Ini ialah pelajaran 1 daripada 4.
Berapa lamakah pelajaran “Perwakilan Graf dan Persediaan Lintasan” diambil?
Kebanyakan pelajaran CoddyKit mengambil masa kira-kira 5–10 minit. Setiap pelajaran ringkas dan interaktif, jadi anda boleh membuat kemajuan secara berterusan dan menyambung tepat dari tempat anda berhenti di web atau aplikasi.
Bolehkah saya menulis dan menjalankan kod dalam pelajaran Persediaan Temu Duga Pengaturcaraan ini?
Ya. Setiap pelajaran Persediaan Temu Duga Pengaturcaraan menyertakan penyunting kod terbina dalam, jadi anda boleh menulis dan menjalankan kod sebenar terus dalam pelayar serta menerima maklum balas kecerdasan buatan serta-merta — tanpa memerlukan persediaan setempat.
Semua pelajaran dalam kursus ini
- Perwakilan Graf dan Persediaan Lintasan
- BFS: Laluan Terpendek dan Lintasan Mengikut Aras
- DFS: Komponen Bersambung dan Isi Banjir
- Pengesanan Kitaran dalam Graf Berarah dan Tidak Berarah