[英雄星球六月集训LeetCode解题日报] 第26日 并查集
日报
- 今天的题都比较朴素,并查集的基础应用。
题目
一、 1559. 二维网格图中探测环
链接: 1559. 二维网格图中探测环
1. 题目描述
2. 思路分析
- 对于网格里每对相邻的格子,如果他们值相同,就可以连接起来。
- 那么我们朝单方向连接即可:
- 对于每个格子我们连接它的上方和左方。
- 不能连右和下的原因是:移到下一个格子的时候会产生重复处理。
3. 代码实现
class UnionFind:
def __init__(self,size):
self.fathers = list(range(size))
def find_father(self,x):
return self._zip_find_father(x)
def _zip_find_father(self,x):
fathers = self.fathers
if fathers[x] != x:
fathers[x] = self._zip_find_father(fathers[x])
return fathers[x]
def union(self,x,y):
x = self.find_father(x)
y = self.find_father(y)
if x == y:
return False
self.fathers[x] = y
return True
class Solution:
def containsCycle(self, grid: List[List[str]]) -> bool:
m,n = len(grid),len(grid[0])
uf = UnionFind(m*n)
for i in range(m):
for j in range(n):
if i > 0:
if grid[i][j] == grid[i-1][j] and not uf.union(i*n+j,(i-1)*n+j):
return True
if j > 0:
if grid[i][j] == grid[i][j-1] and not uf.union(i*n+j,i*n+j-1):
return True
return False
二、 1391. 检查网格中是否存在有效路径
1. 题目描述
2. 思路分析
用并查集处理所有网格,最后检查起始点和结束点是否连通即可(祖宗相同)。
- 这题特殊之处就是要全部特判相邻网格是否连通,v=grid[i][j]:
- v==1时:它只能左右移动,且向左移动时,左边只能连接1、4、6;向右移动时,右边只能连接1、3、5。
- v==其它时,均需特判。
- 为了代码简洁,我们给1-6编号为0-5,用列表储存这6中情况分别对应的方向和方向上能连接的网格值。
- 然后就是遍历所有网格并且让他们移动,判断移动后的网格符合条件就合并家族即可。
3. 代码实现
class UnionFind:
def __init__(self,size):
self.fathers = list(range(size))
def find_father(self,x):
return self._zip_find_father(x)
def _zip_find_father(self,x):
fathers = self.fathers
if fathers[x] != x:
fathers[x] = self._zip_find_father(fathers[x])
return fathers[x]
def union(self,x,y):
x = self.find_father(x)
y = self.find_father(y)
if x == y:
return False
self.fathers[x] = y
return True
def is_same_father(self,x,y):
return self.find_father(x)==self.find_father(y)
class Solution:
def hasValidPath(self, grid: List[List[int]]) -> bool:
m,n = len(grid),len(grid[0])
uf = UnionFind(m*n)
def in_bound(x,y):
return 0<=x<m and 0<=y<n
def hashed(x,y):
return x*n+y
left = {1,4,6}
right = {1,3,5}
top = {2,3,4}
bottom = {2,5,6}
dirs = [ # 6个方块分别能走的方向
[(0,1),(0,-1)], # 右左
[(1,0),(-1,0)], # 下上
[(0,-1),(1,0)], # 左下
[(0,1),(1,0)], # 右下
[(-1,0),(0,-1)], # 上左
[(0,1),(-1,0)], # 右上
]
accepts = [ # 6个方块按方向走后,能连通的合法块
[right,left],
[bottom,top],
[left,bottom],
[right,bottom],
[top,left],
[right,top],
]
for i in range(m):
for j in range(n):
k = grid[i][j] - 1 # 用来索引这个块的属性
for z in range(2): # 走他能走的两个方向
x,y = i+dirs[k][z][0],j+dirs[k][z][1]
accept = accepts[k][z]
if in_bound(x,y) and grid[x][y] in accept:
uf.union(hashed(i,j),hashed(x,y))
return uf.is_same_father(hashed(0,0),hashed(m-1,n-1))
三、 1202. 交换字符串中的元素
链接: 1202. 交换字符串中的元素
1. 题目描述
2. 思路分析
- 我们用并查集把可以互相交换的字符合并为家族。
- 显然同一家族中的字符可以任意摆放。
- 那么我们对每个家族内部排序,然后按顺序填回他们家族的位置即可。
- 为了Python方便比较大小和转换,我先把所有字母变成ASCII码,最后拼接时再转回来。
3. 代码实现
class UnionFind:
def __init__(self,size):
self.fathers = list(range(size))
def find_father(self,x):
return self._zip_find_father(x)
def _zip_find_father(self,x):
fathers = self.fathers
if fathers[x] != x:
fathers[x] = self._zip_find_father(fathers[x])
return fathers[x]
def union(self,x,y):
x = self.find_father(x)
y = self.find_father(y)
if x == y:
return False
self.fathers[x] = y
return True
def is_same_father(self,x,y):
return self.find_father(x)==self.find_father(y)
class Solution:
def smallestStringWithSwaps(self, s: str, pairs: List[List[int]]) -> str:
ss = [ord(c) for c in s]
n = len(ss)
uf = UnionFind(n)
for a,b in pairs:
uf.union(a,b)
familys = defaultdict(list)
for i in range(n):
familys[uf.find_father(i)].append(i)
ans = [0]*n
for ps in familys.values():
ps2 = sorted(ps,key=lambda x:ss[x])
# print (ps,ps2)
for i in range(len(ps)):
ans[ps[i]] = ss[ps2[i]]
return ''.join(chr(c) for c in ans)