# 1559. Detect Cycles in 2D Grid ###### tags: `Leetcode` `Medium` `FaceBook` `Union Find` Link: https://leetcode.com/problems/detect-cycles-in-2d-grid/ ## 思路 $O(mn*c)$ $O(mn)$ ### Union Find $O(mn*c)$ $O(mn)$ 如果发现一条边的两个点的father是同一个的话,就说明成环了 由于grid是二维存的,而并查集是一维的,因此要做个转换 这题用union find效果会比较好,虽然复杂度都是一样的 ### BFS $O(M^2N^2)$ $O(MN)$ 从任意一点开始,对同一个value的所有像素做常规的遍历。如果遍历的过程中遇到了之前访问过的格子,那么就是有环。注意遍历的过程中不能走“回头路”,即从A遍历到B,那么从B开始的遍历就不能包括A。 我们记录每个点是通过走哪个方向从它的上一个点来的,那么从这个点出发往外走的时候就不能走相反方向 ### DFS DFS 但是要注意的一点是由于要找circle,所以一旦回到被visit过的点的时候就返回true(说明有circle),这就导致我们不能在访问一个点,并且找它的邻居的时候把它来的时候经过的那个点又加进来,这样无论如何都会有circle 例如: a-a 先访问左边的a,再访问右边的a,在访问右边的a的时候,不能回去再找到左边的a了,因此要记录来时经过的那个node 一开始的思路是在跑while回圈的时候用prev记录上一个经过的点,那么curr在找neighbor的时候不能找和prev一模一样的点,但是后来发现由于是用dfs,所以上一个访问的点不一定是curr的neighbor,因此要用递归做 ## Code ### Union Find ```java= class Solution { private int[] fa; private int[] size; public boolean containsCycle(char[][] grid) { int r = grid.length; int c = grid[0].length; fa = new int[r*c]; size = new int[r*c]; for(int i = 0;i < r;i++){ for(int j = 0;j < c;j++){ fa[i*c+j] = i*c+j; size[i*c+j] = 1; } } for(int i = 0;i < r;i++){ for(int j = 0;j < c;j++){ if(i+1<r && grid[i][j] == grid[i+1][j]){ if(combine(i*c+j, (i+1)*c+j)) return true; } if(j+1<c && grid[i][j] == grid[i][j+1]){ if(combine(i*c+j, i*c+j+1)) return true; } } } return false; } public int find(int a){ if(a == fa[a]) return a; else return fa[a] = find(fa[a]); } public boolean combine(int a, int b){ a = find(a); b = find(b); if(a==b) return true; if(size[a]>size[b]){ fa[b] = a; size[a] += size[b]; } else{ fa[a] = b; size[b] += size[a]; } return false; } } ``` ### BFS ```java= class Solution { public boolean containsCycle(char[][] grid) { int m = grid.length, n = grid[0].length; int[][] dirs = new int[][]{{1,0},{-1,0},{0,1},{0,-1}}; boolean[][] visited = new boolean[m][n]; for(int i=0; i<m; i++){ for(int j=0; j<n; j++){ if(visited[i][j]) continue; Queue<int[]> q = new LinkedList<>(); q.add(new int[]{i,j,-1}); while(!q.isEmpty()){ int[] curr = q.poll(); for(int k=0; k<dirs.length; k++){ int[] dir = dirs[k]; int newx = curr[0]+dir[0]; int newy = curr[1]+dir[1]; if(k==0 && curr[2]==1) continue; if(k==1 && curr[2]==0) continue; if(k==2 && curr[2]==3) continue; if(k==3 && curr[2]==2) continue; if(newx>=0 && newy>=0 && newx<m && newy<n && grid[newx][newy]==grid[i][j]){ if(visited[newx][newy]) return true; visited[newx][newy] = true; q.add(new int[]{newx, newy, k}); } } } } } return false; } } ``` ### DFS ```java= class Solution { int[][] directions = new int[][]{{-1,0},{1,0},{0,1},{0,-1}}; public boolean containsCycle(char[][] grid) { int r = grid.length; int c = grid[0].length; boolean[][] visited = new boolean[r][c]; for(int i = 0;i < r;i++){ for(int j = 0;j < c;j++){ if(!visited[i][j]){ if(dfs(grid[i][j], new int[]{i, j}, new int[]{-1, -1}, visited, grid, r, c)) return true; } } } return false; } private boolean dfs(char startChar, int[] curr, int[] prev, boolean[][] visited, char[][] grid, int r, int c){ visited[curr[0]][curr[1]] = true; List<int[]> nextPos = getNext(curr, r, c); for(int[] next:nextPos){ if(!(next[0]==prev[0] && next[1]==prev[1])){ if(grid[next[0]][next[1]]==startChar){ if(visited[next[0]][next[1]]){ return true; } else{ visited[next[0]][next[1]] = true; if(dfs(startChar, next, curr, visited, grid, r, c)) return true; } } } } return false; } private List<int[]> getNext(int[] curr, int r, int c){ List<int[]> ans = new ArrayList<>(); for(int i = 0;i < directions.length;i++){ int newi = curr[0]+directions[i][0]; int newj = curr[1]+directions[i][1]; if(newi>=0 && newi<r && newj>=0 && newj<c){ ans.add(new int[]{newi, newj}); } } return ans; } } ```