題目描述:給定一個 m × n 的全水網格,依序執行一系列 addLand(r, c) 操作,每次將 (r, c) 格子從水變成陸地。回傳一個陣列,記錄每次操作後當前的島嶼數量。兩個格子若上下左右相鄰且都是陸地,則屬於同一座島嶼。
解題思路:
此題的核心在於動態連通性:每次新增陸地後,需要快速更新島嶼計數。Union-Find(並查集)天生適合此場景。
r * cols + c 將二維座標壓縮為一維節點 ID,方便作為並查集的索引。parent[] 和 rank[] 陣列,初始所有格子為未啟用(可用 -1 標記)。維護一個 islandCount 計數器。islandCount++(暫時視為孤立島嶼)。union 操作,islandCount--。find 中遞迴更新 parent)與按秩合併(union 時比較 rank),使每次操作近乎 O(α(n)) — 逆阿克曼函數,實用上視為常數。這樣每次 addLand 的均攤時間複雜度接近 O(1),整體處理 k 次操作為 O(k · α(m*n))。
時間複雜度:O(k · α(m·n)),其中 k 為操作次數,α 為逆阿克曼函數。路徑壓縮 + 按秩合併使每次 find/union 均攤 O(α(n)),實務上視為常數,整體接近 O(k)。
空間複雜度:O(m·n) — 儲存 parent 和 rank 兩個長度為 m·n 的陣列。輸出結果陣列 O(k) 另計。
1. Initialize parent[] = -1 (water), rank[] = 0, islandCount = 0
2. For each position (r, c) in positions:
a. Compute id = r * cols + c
b. If parent[id] != -1 (duplicate): append islandCount to result, continue
c. Set parent[id] = id, islandCount++
d. For each of 4 directions (nr, nc):
- Skip if out of bounds
- nid = nr * cols + nc
- If parent[nid] == -1: skip (water)
- Else: unite(id, nid) which decrements islandCount if roots differ
e. Append islandCount to result
3. Return result
find(x):
If parent[x] != x: parent[x] = find(parent[x]) // path compression
Return parent[x]
unite(a, b):
a = find(a), b = find(b)
If a == b: return
Merge smaller rank under larger; islandCount--BFS/DFS 暴力重算 O(k · m·n):每次 addLand 後重新從頭 BFS/DFS 計算所有島嶼。優點是實作直觀,缺點是每次操作都要掃描整個網格,k 次操作總時間為 O(k·m·n),當 k 與 m·n 都很大時效率極差,無法通過本題。
雜湊表版 Union-Find(稀疏優化):當 positions 數量遠小於 m·n 時,可用 unordered_map<int,int> 代替固定大小陣列作為 parent,只為實際出現的陸地節點分配空間。優點是節省記憶體(從 O(m·n) 降至 O(k)),缺點是雜湊表常數較大,在 k 與 m·n 相近時不如陣列版本。
離線排序(Kruskal 思路):若可以離線處理,將所有 addLand 操作一次讀入,再按時間順序建圖並用 Kruskal 合併。優點是可以利用邊排序的性質做進一步最佳化,但不支援在線查詢,且不適用本題的即時回傳需求。