题目:
假设你设计一个游戏,用一个 m 行 n 列的 2D 网格来存储你的游戏地图。
起始的时候,每一个格子的地形都被默认标记为「水」。咱们能够经过使用 addLand 进行操作,将位置 (row, col) 的「水」变成「陆地」。
你将会被给定一个列表,来记录全部须要被操作的位置,而后你须要返回计算出来 每次 addLand 操作后岛屿的数量。
注意:一个岛的定义是被「水」包围的「陆地」,经过水平方向或者垂直方向上相邻的陆地链接而成。
输入:m = 3, n = 3, positions = [[0,0],[0,1],[1,2],[2,1]]
输出:[1,1,2,3]
解释:
起初,二维网格 grid 被全部注入「水」。(0 代表「水」,1 代表「陆地」)
- 操作 #1:addLand(0, 0) 将 grid[0][0] 的水变为陆地。此时存在 1 个岛屿。
- 操作 #2:addLand(0, 1) 将 grid[0][1] 的水变为陆地。此时存在 1 个岛屿。
- 操作 #3:addLand(1, 2) 将 grid[1][2] 的水变为陆地。此时存在 2 个岛屿。
- 操作 #4:addLand(2, 1) 将 grid[2][1] 的水变为陆地。此时存在 3 个岛屿。
思路:
并查集:
parent:字典,记录父子关系
cnt:记录并查集的个数
解答:
class UF:
def __init__(self, M):
#字典,记录父子关系:{儿子:父亲}
self.parent = {}
# cnt:表示并查集中的集合个数
self.cnt = 0
#将idx加入并查集中
def add(self,idx):
if idx not in self.parent:
self.parent[idx]=idx
self.cnt+=1
# 判断x所属集合
def find(self, x):
while x != self.parent[x]:
x = self.parent[x]
return x
# 合并p和q所在的集合,新元素所在集合纳入旧元素所在集合中
def union(self, p, q):
if self.connected(p, q):
return
set_p = self.find(p)
set_q = self.find(q)
if set_p!=set_q:
self.parent[set_p]=set_q
self.cnt-=1
# 判断两元素是否属于同一集合
def connected(self, p, q):
return self.find(p) == self.find(q)
#填海造陆,0海,1陆
class Solution:
def numIsLand2(self,m,n,positions):
def index(i,j):
return i*n+j
dir = [[-1, 0], [1, 0], [0, -1], [0, 1]]
grid=[[0]*n for _ in range(m)]
uf=UF(m*n)
res=[]
for x,y in positions:
cur=index(x,y)
uf.add(cur)
for dx,dy in dir:
ng=index(x+dx,y+dy)
#如果邻居已被填为大陆,则将当前元素合并到邻居中
if ng in uf.parent:
uf.union(cur,ng)
res.append(uf.cnt)
return res
if __name__=='__main__':
solution=Solution()
m,n=3,3
positions=[[0,0],[0,1],[1,2],[2,1]]
res=solution.numIsLand2(m,n,positions)
print(res)
思维扩展1:
如果要返回每个岛屿的大小,则并查集需要新增一个size.
示例返回:[[1, [1]], [1, [2]], [2, [2, 1]], [3, [2, 1, 1]]]
class UF:
def __init__(self, M):
#字典,记录父子关系{儿子:父亲}
self.parent = {}
#size:记录集合大小{元素:元素集合(包含当前元素及其孩子)的大小}
self.size={}
# cnt:表示并查集中的集合个数
self.cnt = 0
#将idx加入并查集中
def add(self,idx):
if idx not in self.parent:
self.parent[idx]=idx
self.size[idx]=1
self.cnt+=1
# 判断x所属集合
def find(self, x):
while x != self.parent[x]:
x = self.parent[x]
return x
# 合并p和q所在的集合,新元素所在集合纳入旧元素所在集合中
def union(self, p, q):
if self.connected(p, q):
return
set_p = self.find(p)
set_q = self.find(q)
if set_p!=set_q:
self.parent[set_p]=set_q
self.size[set_q]+=self.size[set_p]
self.cnt-=1
# 判断两元素是否属于同一集合
def connected(self, p, q):
return self.find(p) == self.find(q)
#填海造陆,0海,1陆
class Solution:
def numIsLand2(self,m,n,positions):
def index(i,j):
return i*n+j
dir = [[-1, 0], [1, 0], [0, -1], [0, 1]]
grid=[[0]*n for _ in range(m)]
uf=UF(m*n)
res=[]
for x,y in positions:
cur=index(x,y)
uf.add(cur)
for dx,dy in dir:
ng=index(x+dx,y+dy)
#如果邻居已被填为大陆,则将当前元素合并到邻居中
if ng in uf.parent:
uf.union(cur,ng)
print(uf.parent,uf.size,uf.cnt)
landArea=sorted(uf.size.values(),key=lambda x:-x)
landNum=uf.cnt
#res中的每一项代表:[当前岛屿个数,每个岛屿的大小]
res.append([landNum,landArea[:landNum]])
return res
if __name__=='__main__':
solution=Solution()
m,n=3,3
positions=[[0,0],[0,1],[1,2],[2,1]]
res=solution.numIsLand2(m,n,positions)
print(res)
思维扩展2:
返回岛屿个数,即每个岛屿的岛主和岛屿大小
示例返回:[[1, [(0, 1)]], [1, [(0, 2)]], [2, [(0, 2), (5, 1)]], [3, [(0, 2), (5, 1), (7, 1)]]]
class UF:
def __init__(self, M):
#字典,记录父子关系{儿子:父亲}
self.parent = {}
#size:记录集合大小{元素:元素集合(包含当前元素及其孩子)的大小}
self.size={}
# cnt:表示并查集中的集合个数
self.cnt = 0
#将idx加入并查集中
def add(self,idx):
if idx not in self.parent:
self.parent[idx]=idx
self.size[idx]=1
self.cnt+=1
# 判断x所属集合
def find(self, x):
while x != self.parent[x]:
x = self.parent[x]
return x
# 合并p和q所在的集合,新元素所在集合纳入旧元素所在集合中
def union(self, p, q):
if self.connected(p, q):
return
set_p = self.find(p)
set_q = self.find(q)
if set_p!=set_q:
self.parent[set_p]=set_q
self.size[set_q]+=self.size[set_p]
self.cnt-=1
# 判断两元素是否属于同一集合
def connected(self, p, q):
return self.find(p) == self.find(q)
#填海造陆,0海,1陆
class Solution:
# 方法1:返回[(岛主,对应岛屿的大小)]
# 直接按岛屿大小降序排序,返回符合要求的前uf.cnt的即可
def getLandInfo1(self,uf):
# 按岛屿大小降序排列
landArea = sorted(uf.size.items(), key=lambda x: -x[1])
tmp = [] # 记录岛主及其岛屿面积的大小
while len(tmp) != uf.cnt:
for landRoot, area in landArea:
if uf.find(landRoot) == landRoot:
tmp.append((landRoot, area))
return tmp
#方法2:返回[(岛主,对应岛屿的大小)]
#需要对父子集合遍历并判断,若当前元素为岛主,即加入结果集。结果集大小等于uf.cnt时即可返回
def getLandInfo2(self,uf):
tmp=[]
for son,parent in uf.parent.items():
if son==parent:
tmp.append((parent,uf.size[parent]))
if len(tmp)==uf.cnt:
break
return tmp
def numIsLand2(self,m,n,positions):
def index(i,j):
return i*n+j
dir = [[-1, 0], [1, 0], [0, -1], [0, 1]]
grid=[[0]*n for _ in range(m)]
uf=UF(m*n)
ans=[]
for x,y in positions:
cur=index(x,y)
uf.add(cur)
for dx,dy in dir:
ng=index(x+dx,y+dy)
#如果邻居已被填为大陆,则将当前元素合并到邻居中
if ng in uf.parent:
uf.union(cur,ng)
#cur_landInfo=self.getLandInfo1(uf)
cur_landInfo = self.getLandInfo2(uf)
ans.append([uf.cnt,cur_landInfo])
#print(ans)
return ans
if __name__=='__main__':
solution=Solution()
m,n=3,3
positions=[[0,0],[0,1],[1,2],[2,1]]
ans=solution.numIsLand2(m,n,positions)
print(ans)