并查集解决岛屿问题,是一个经典的算法应用。本篇文章旨在帮助大家理解并查集算法,并实现在岛屿问题上的应用。
- 并查集算法介绍
并查集是一种用于解决动态连通性问题的高效数据结构,为便于解释我们以岛屿问题为例,说明并查集的工作原理。
- 岛屿问题
给定一个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
最后遍历整个二维数组中的陆地,看它右边和下边的点需不需要合并,最后返回岛屿数目。