並查集解決島嶼問題,是一個經典的算法應用。本篇文章旨在幫助大家理解並查集算法,並實現在島嶼問題上的應用。
- 並查集算法介紹
並查集是一種用於解決動態連通性問題的高效數據結構,爲便於解釋我們以島嶼問題爲例,說明並查集的工作原理。
- 島嶼問題
給定一個m*n的二位數組,其中1代表陸地,0代表海洋,如果一個陸地塊的上下左右鄰接着另一個陸地塊,則稱它們爲一個島嶼。提問在給定二維數組中有幾個島嶼?(輸入輸出樣例如下)
要解決這個問題很簡單,我們只需要把每一塊陸地計算出它所屬的島嶼標號(分組),然後統計所有標號的數目就是島嶼的數目。在最開始的時候我們認爲每一個陸地塊都是一個島嶼,之後我們通過“並”和“查”兩個操作去不斷地合併陸地塊,並解決問題。
1.“查”算法
def find(x):
if parent[x]!= x:
return find(parent[x])
return parent[x]
但是這樣做有一個問題,隨着不同子集不斷地合併,查詢的複雜度會脫離O1級別,最壞情況下達到鏈表ON級別。這種情況可以使用“路徑壓縮”進行優化,就是把查找過程的所有路徑上的點的parent,改成它們共同的祖先。
def find(x):
if parent[x]!=x: parent[x]=find[parent[x]] #遞歸寫法,最終會吧所有點連在根節點上
#構造一棵扁平的樹
return parent[x]
還有非遞歸寫法,就是先用一個while循環找到祖先,再把路徑上的點的parent都改成祖先。
2."並"算法
def union(x,y):
xroot, yroot = find(x),find(y)
if xroot == yroot: return
parent[xroot] = yroot
self.count -= 1
這裏每執行一次並,島嶼數目減1。在實際中我們可以優化並算法,爲每棵樹維護一個深度,每次把深度小的樹併到深度大的樹上。
for i in range(row):
for j in range(col):
if grid[i][j] == '0':
continue
index = i*col + j
if j < col-1 and grid[i][j+1] == '1':
union(index, index+1)
if i < row-1 and grid[i+1][j] == '1':
union(index, index+col)
return self.count
最後遍歷整個二維數組中的陸地,看它右邊和下邊的點需不需要合併,最後返回島嶼數目。