1559. 二维网格图中探测环
给你一个二维字符网格数组 grid
,大小为 m x n
,你需要检查 grid
中是否存在 相同值 形成的环。
一个环是一条开始和结束于同一个格子的长度 大于等于 4 的路径。对于一个给定的格子,你可以移动到它上、下、左、右四个方向相邻的格子之一,可以移动的前提是这两个格子有 相同的值 。
同时,你也不能回到上一次移动时所在的格子。比方说,环 (1, 1) -> (1, 2) -> (1, 1)
是不合法的,因为从 (1, 2)
移动到 (1, 1)
回到了上一次移动时的格子。
如果 grid
中有相同值形成的环,请你返回 true
,否则返回 false
。
示例 1:
输入:grid = [["a","a","a","a"],["a","b","b","a"],["a","b","b","a"],["a","a","a","a"]] 输出:true 解释:如下图所示,有 2 个用不同颜色标出来的环:
示例 2:
输入:grid = [["c","c","c","a"],["c","d","c","c"],["c","c","e","c"],["f","c","c","c"]] 输出:true 解释:如下图所示,只有高亮所示的一个合法环:
示例 3:
输入:grid = [["a","b","b"],["b","z","b"],["b","b","a"]] 输出:false
提示:
m == grid.length
n == grid[i].length
1 <= m <= 500
1 <= n <= 500
grid
只包含小写英文字母。原站题解
golang 解法, 执行用时: 56 ms, 内存消耗: 60.7 MB, 提交时间: 2023-10-10 15:44:11
func containsCycle(grid [][]byte) bool { m := len(grid) n := len(grid[0]) visited := make([][]bool, m) for i:=0; i<m; i++ { visited[i] = make([]bool, n) } for i:=0; i<m; i++ { for j:=0; j<n; j++ { if visited[i][j] { continue } if dfs(grid, m, n, i, j, -1, -1, visited) { return true } } } return false } var ( moves = [][2]int{[2]int{0, 1}, [2]int{0, -1}, [2]int{1, 0}, [2]int{-1, 0}} ) func dfs(grid [][]byte, m, n, x, y, lastX, lastY int, visited [][]bool) bool { if visited[x][y] { return true } visited[x][y] = true for _, move := range moves { nextX := x+move[0] nextY := y+move[1] if nextX >= m || nextX < 0 || nextY >= n || nextY < 0 { // out of bound continue } if nextX == lastX && nextY == lastY { // cannot go to last move continue } if grid[nextX][nextY] != grid[x][y] { continue } ans := dfs(grid, m, n, nextX, nextY, x, y, visited) if ans { return true } } return false }
python3 解法, 执行用时: 564 ms, 内存消耗: 59.8 MB, 提交时间: 2023-10-10 15:43:35
class UnionFind: def __init__(self, n: int): self.n = n self.setCount = n self.parent = list(range(n)) self.size = [1] * n def findset(self, x: int) -> int: if self.parent[x] == x: return x self.parent[x] = self.findset(self.parent[x]) return self.parent[x] def unite(self, x: int, y: int): if self.size[x] < self.size[y]: x, y = y, x self.parent[y] = x self.size[x] += self.size[y] self.setCount -= 1 def findAndUnite(self, x: int, y: int) -> bool: parentX, parentY = self.findset(x), self.findset(y) if parentX != parentY: self.unite(parentX, parentY) return True return False 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 and grid[i][j] == grid[i - 1][j]: if not uf.findAndUnite(i * n + j, (i - 1) * n + j): return True if j > 0 and grid[i][j] == grid[i][j - 1]: if not uf.findAndUnite(i * n + j, i * n + j - 1): return True return False
cpp 解法, 执行用时: 232 ms, 内存消耗: 50.2 MB, 提交时间: 2023-10-10 15:42:59
class UnionFind { public: vector<int> parent; vector<int> size; int n; int setCount; public: UnionFind(int _n): n(_n), setCount(_n), parent(_n), size(_n, 1) { iota(parent.begin(), parent.end(), 0); } int findset(int x) { return parent[x] == x ? x : parent[x] = findset(parent[x]); } void unite(int x, int y) { if (size[x] < size[y]) { swap(x, y); } parent[y] = x; size[x] += size[y]; --setCount; } bool findAndUnite(int x, int y) { int parentX = findset(x); int parentY = findset(y); if (parentX != parentY) { unite(parentX, parentY); return true; } return false; } }; class Solution { public: bool containsCycle(vector<vector<char>>& grid) { int m = grid.size(); int n = grid[0].size(); UnionFind uf(m * n); for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { if (i > 0 && grid[i][j] == grid[i - 1][j]) { if (!uf.findAndUnite(i * n + j, (i - 1) * n + j)) { return true; } } if (j > 0 && grid[i][j] == grid[i][j - 1]) { if (!uf.findAndUnite(i * n + j, i * n + j - 1)) { return true; } } } } return false; } };
java 解法, 执行用时: 10 ms, 内存消耗: 83.7 MB, 提交时间: 2023-10-10 15:42:33
class Solution { public boolean containsCycle(char[][] grid) { int h = grid.length; int w = grid[0].length; DSU dsu = new DSU(w * h); for (int i = 0; i < h; ++i) { for (int j = 0; j < w; ++j) { char cur = grid[i][j]; // 向右搜索 if (j + 1 < w && cur == grid[i][j + 1]) { if (dsu.union(i * w + j, i * w + j + 1)) { return true; } } // 向下搜索 if (i + 1 < h && cur == grid[i + 1][j]) { if (dsu.union(i * w + j, (i + 1) * w + j)) { return true; } } } } return false; } class DSU { int[] parent; public DSU(int N) { parent = new int[N]; for (int i = 0; i < N; ++i) { parent[i] = i; } } public int find(int x) { if (parent[x] != x) { parent[x] = find(parent[x]); } return parent[x]; } /** * 若合并前,x和y的parent相同,则表示形成环,返回true。 * * @param x * @param y * @return */ public boolean union(int x, int y) { int parentX = find(x); int parentY = find(y); if (parentX == parentY) { return true; } if (parentX < parentY) { parent[parentY] = parentX; } else { parent[parentX] = parentY; } return false; } } }
java 解法, 执行用时: 16 ms, 内存消耗: 83.9 MB, 提交时间: 2023-10-10 15:42:08
class Solution { boolean[][] visited; char[][] grid; int m, n; boolean hasRing; public boolean containsCycle(char[][] grid) { this.grid = grid; m = grid.length; n = grid[0].length; visited = new boolean[m][n]; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (!visited[i][j]){ dfs(i, j, grid[i][j], 'L'); if (hasRing) return true; } } } return false; } private void dfs(int i, int j, char ch, char from) { if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] != ch){ return; } if (visited[i][j]) { hasRing = true; return; } visited[i][j] = true; if (from != 'L') dfs(i, j - 1, ch, 'R'); if (from != 'R') dfs(i, j + 1, ch, 'L'); if (from != 'U') dfs(i - 1, j, ch, 'D'); if (from != 'D') dfs(i + 1, j, ch, 'U'); } }
java 解法, 执行用时: 23 ms, 内存消耗: 72.6 MB, 提交时间: 2023-10-10 15:41:41
/** * bfs * 1. 每次从队列中取出一个节点时,记录当前队列大小size,初始化neigbors = 0 * 2. 在上下左右四个方向遍历,如果相邻节点没有越界且与当前字符相同,++neighbors, * 如果没访问过,加入队列,visited赋值true * 3. 遍历完四个方向后,如果neigbors - 1== queue.size() - size, * (-1是减去上一次访问过,并将当前节点入队的节点),说明新入队的邻居之前都没有访问过 * 4. 如果neigbors - 1 > queue.size() - size,说明遍历到了其他方向遍历过来的节点,那么可以形成环 */ class Solution { public boolean containsCycle(char[][] grid) { m = grid.length; n = grid[0].length; visited = new boolean[m][n]; for (int i = 0; i < m; ++i) for (int j = 0; j < n; ++j) if (!visited[i][j] && bfs(i, j, grid)) return true; return false; } private boolean bfs(int i, int j, char[][] grid) { queue.offer(new int[]{i, j}); visited[i][j] = true; while (!queue.isEmpty()) { int[] node = queue.poll(); int neibors = 0, size = queue.size(); for (int[] dir : dirs) { int row = node[0] + dir[0], col = node[1] + dir[1]; if (isValid(row, col) && grid[row][col] == grid[node[0]][node[1]]) { ++neibors; if (!visited[row][col]) { queue.offer(new int[]{row, col}); visited[row][col] = true; } } } if (neibors - 1 > queue.size() - size) return true; } return false; } int m, n; boolean[][] visited; Queue<int[]> queue = new LinkedList<>(); int[][] dirs = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; private boolean isValid(int i, int j) { return i >=0 && j >= 0 && i < m && j < n; } }