题目难度: 中等
原题链接
今天我们再来做一道并查集的变种问题. 个人觉得这道题非常有趣, 特别是它的思考过程; 而且除了并查集之外还可以用其他的做法, 可以帮助大家扩展思维. 大家在我的公众号"每日精选算法题"中的聊天框中回复 并查集 就能看到该系列当前已经更新的文章了
大家有什么想法建议和反馈的话欢迎随时交流, 包括但不限于公众号聊天框/知乎私信评论等等~
在由 1 x 1 方格组成的 N x N 网格 grid 中,每个 1 x 1 方块由 /、 或空格构成。这些字符会将方块划分为一些共边的区域。
(请注意,反斜杠字符是转义的,因此 用 表示。)。
返回区域的数目。
[
" /",
"/ "
]
2
[
" /",
" "
]
1
[
"/",
"/"
]
4
/
表示 /,而 /
表示 /。)4*4*N*N
次, 并查集中存4*N*N
个元素, 每次合并和查询操作需要 O(log(4*N*N)) = O(log(N))
的时间, 省略掉常系数, 总时间复杂度就是 O(N^2logN)4*N*N
个元素class Solution:
def regionsBySlashes(self, grid: List[str]) -> int:
# 由于是斜杠, 所以可以将每个格子根据两个对角线划分为四个块, 分别是上下左右(可以参考示例3的图的四个块)
# 方法1: 并查集
up, down, left, right = 0, 1, 2, 3
relations = {
' ': {
# 表示当前格子是' '的情况下, 4个块各自可以连通的部分, 正斜杠和反斜杠的情况类似
# 元组中的前两个值表示和当前的行和列的差值, 如果都是0表示在同一个格子里, 而(-1,0)表示在上面一个格子里, 即(r-1,c)
# 对于相邻格子而言, 所有的up都可以与上一个格子的down相连, left和左边格子的right相连, 以此类推
# 而对于同一格子内的四个块, 如果是空格的话表示互相连通, 正斜杠的话说明left和up连通, 反斜杠的话说明up和right连通, 以此类推就可以得出这个连通关系字典了
up: [(0, 0, down), (0, 0, left), (0, 0, right), (-1, 0, down)],
down: [(0, 0, up), (0, 0, left), (0, 0, right), (1, 0, up)],
left: [(0, 0, up), (0, 0, down), (0, 0, right),
(0, -1, right)],
right: [(0, 0, up), (0, 0, down), (0, 0, left), (0, 1, left)],
},
'/': {
up: [(0, 0, left), (-1, 0, down)],
down: [(0, 0, right), (1, 0, up)],
left: [(0, 0, up), (0, -1, right)],
right: [(0, 0, down), (0, 1, left)],
},
'': {
up: [(0, 0, right), (-1, 0, down)],
down: [(0, 0, left), (1, 0, up)],
left: [(0, 0, down), (0, -1, right)],
right: [(0, 0, up), (0, 1, left)],
}
}
n = len(grid)
# 并查集部分, 进行了一些改动保存三维状态: r,c,d, 来代表每个块 (r-行号, c-列号, d-方向)
pre = {}
def find(r, c, d):
if (r, c, d) not in pre:
pre[r, c, d] = (r, c, d)
elif pre[r, c, d] != (r, c, d):
pr, pc, pb = pre[r, c, d]
pre[r, c, d] = find(pr, pc, pb)
return pre[r, c, d]
def union(r1, c1, d1, r2, c2, d2):
pre[find(r1, c1, d1)] = find(r2, c2, d2)
for r in range(n):
for c in range(n):
char = grid[r][c]
for d in (up, down, left, right):
# 遍历当前格子的四个块, 用d表示上下左右
for neighbor in relations[char][d]:
# dr和dc表示邻居格子与当前格子的r和c的delta值, 可选范围为-1/0/1
dr, dc, dd = neighbor
# rr和cc是实际的邻居rc, 需要判断是否是有效值
rr, cc = r + dr, c + dc
if 0 <= rr < n and 0 <= cc < n:
union(r, c, d, rr, cc, dd)
regions = set()
for r in range(n):
for c in range(n):
for d in (up, down, left, right):
# 将每个块的祖先放在集合中, 相同祖先的块即表示这些块之间相互连通
# 最后集合的元素个数即为结果
regions.add(find(r, c, d))
return len(regions)
4*N*N
个元素class Solution:
def regionsBySlashes(self, grid: List[str]) -> int:
# 方法2: BFS
# 同样方法预处理连通关系
up, down, left, right = 0, 1, 2, 3
relations = {
' ': {
up: [(0, 0, down), (0, 0, left), (0, 0, right), (-1, 0, down)],
down: [(0, 0, up), (0, 0, left), (0, 0, right), (1, 0, up)],
left: [(0, 0, up), (0, 0, down), (0, 0, right),
(0, -1, right)],
right: [(0, 0, up), (0, 0, down), (0, 0, left), (0, 1, left)],
},
'/': {
up: [(0, 0, left), (-1, 0, down)],
down: [(0, 0, right), (1, 0, up)],
left: [(0, 0, up), (0, -1, right)],
right: [(0, 0, down), (0, 1, left)],
},
'': {
up: [(0, 0, right), (-1, 0, down)],
down: [(0, 0, left), (1, 0, up)],
left: [(0, 0, down), (0, -1, right)],
right: [(0, 0, up), (0, 1, left)],
}
}
n = len(grid)
# visit 集合v判断当前块是否已经被访问过
v = set()
res = 0
def bfs(r, c, d):
q = [(r, c, d)]
for x in q:
r, c, d = x
char = grid[r][c]
for nex in relations[char][d]:
# 找到当前块的邻居, 如果其有效且尚未被访问, 将其加到q以及v集合中
dr, dc, dd = nex
# rr和cc是实际的邻居rc, 需要判断是否是有效值
rr, cc = r + dr, c + dc
if 0 <= rr < n and 0 <= cc < n and (rr, cc, dd) not in v:
# 找到有效邻居了, 加入q和v
q.append((rr, cc, dd))
v.add((rr, cc, dd))
for r in range(n):
for c in range(n):
for d in range(4):
if (r, c, d) not in v:
# 找到一个还不属于任何区域的块, 以它为起点BFS, 同时res+1
res += 1
bfs(r, c, d)
return res
大家可以在下面这些地方找到我~
我的知乎专栏
我的 CSDN
我的 Leetcode
我的牛客网博客
我的公众号: 每日精选算法题, 欢迎大家扫码关注~