這是資料結構的最後一篇,明天開始進入本系列的重點驗算法,前面幾天,我們介紹了各種樹結構 Binary Tree、Balanced Tree,以及其他衍生樹。而樹是一種特殊的圖結構,今天我們正式進入 Graph 的世界。
圖是比樹更一般化的資料結構,能表示任何實體之間的關係,例如:
理解圖結構,幾乎等於打開了資料結構與演算法的第二扇大門。
圖 (Graph) 由以下兩個元素組成:
數學上通常記為:
$$
G = (V, E)
$$
使用二維陣列 $A[i][j]$ 表示是否存在一條從節點 i 到節點 j 的邊。
建立一個「無向圖 (undirected graph) 的鄰接矩陣 (adjacency matrix)」表示法:
# 範例: 無向圖 0-1, 0-2, 1-2, 2-3
n = 4
adj_matrix = [[0]*n for _ in range(n)]
edges = [(0,1),(0,2),(1,2),(2,3)]
for u,v in edges:
adj_matrix[u][v] = 1
adj_matrix[v][u] = 1 # 無向圖
print("Adjacency Matrix:")
for row in adj_matrix:
print(row)
使用 list 或 dict 紀錄每個節點相鄰的節點。
建立一個「無向圖 (undirected graph) 的鄰接串列 (Adjacency List)」表示法:
from collections import defaultdict
n = 4
adj_list = defaultdict(list)
edges = [(0,1),(0,2),(1,2),(2,3)]
for u,v in edges:
adj_list[u].append(v)
adj_list[v].append(u) # 無向圖
print("Adjacency List:")
for k,v in adj_list.items():
print(k, ":", v)
圖的遍歷是所有圖演算法的基礎。
使用遞迴或 Stack,先往一條路走到底再回頭。
def dfs(graph, start, visited=None):
if visited is None:
visited = set()
visited.add(start)
print(start, end=" ")
for neighbor in graph[start]:
if neighbor not in visited:
dfs(graph, neighbor, visited)
dfs(adj_list, 0) # 輸出: 0 1 2 3
使用 Queue,從起點開始一層一層向外擴展。
from collections import deque
def bfs(graph, start):
visited = set([start])
queue = deque([start])
while queue:
node = queue.popleft()
print(node, end=" ")
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
bfs(adj_list, 0) # 輸出: 0 1 2 3
今天我們進入了圖結構 (Graph),它比前面的線性結構與樹結構更通用,能夠描述任意的關聯關係。從 鄰接矩陣到鄰接串列,再到 DFS 與 BFS,這些都是後續更複雜圖演算法的基礎。
圖結構的強大之處,在於它幾乎無所不在:
理解圖結構,能讓我們從資料結構的「儲存與操作」進一步走向「建模與分析」。