Leetcode每日一题 —— 1559. 二维网格图中探测环

2026-04-29 09:242阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐
问题描述:
力扣 LeetCode

1559. 二维网格图中探测环 - 力扣(LeetCode)

1559. 二维网格图中探测环 - 给你一个二维字符网格数组grid,大小为m x n,你需要检查grid中是否存在 相同值 形成的环。 一个环是一条开始和结束于同一个格子的长度 大于等于 4的路径。对于一个给定的格子,你可以移动到它上、下、左、右四个方向相邻的格子之一,可以移动的前提是这两个格子有 相同的值。 同时,你也不能回到上一次移动时所在的格子。比方说,环(1, 1) -> (1, 2) -> (1, 1)是不合法的,因为从 (1, 2)移动到 (1, 1)...


思路

比较典型的无向图判环题,常用的思想有 DFS / BFS / 并查集,这里咱就用并查集来做了。

先注意题目中两个条件:

  1. 走回头路不算环路,也就不能往回走。
  2. 环路径的长度要 \ge 4 才算。其实在第 1 点条件的限制下这点是必然成立的,最小的环必须要有四个格子组成。

所以这题只需要保证不走回头路,进行判环即可。

用并查集判环,要不走回头路的话,我们在扫描网格时从上至下、从左至右,对于每个格子可以只尝试合并其右方或者下方的格子,按这样的逻辑处理必然是不会有回头路滴。

如此扫描直至某个网格与其右边或者下边的网格在同一个集合中,则满足题目要求,返回 true


代码

class Solution { public: bool containsCycle(vector<vector<char>>& grid) { // 比较重要的是这种环路的长度还必须 >=4 才算 // 其实不难,可以用并查集来执行合并和判环,如果有环就看对应块中元素数量是不是 >=4 // 要构成环,最小的环必然是四个方格组成的 int m=grid.size(),n=grid[0].size(); // 初始化并查集 vector<int> parents(m*n,-1),sizes(m*n,1); auto find=[&](auto&& self, int x) -> int { // 带路径压缩的查找 return parents[x]==-1 ? x : parents[x]=self(self, parents[x]); }; // 合并,返回是否执行了合并 auto merge=[&](int x1,int x2) -> bool { int root1=find(find,x1),root2=find(find,x2); if(root1==root2){ // 已经合并 return false; } // 否则启发式合并,小树并入大树 if(sizes[root1]<sizes[root2]){ swap(root1,root2); } parents[root2]=root1; sizes[root1]+=sizes[root2]; return true; }; // 获得集合大小 // auto getSize=[&](int x){ // return sizes[find(find,x)]; // }; // 扫描数组执行判环 int drcts[][2]={ {0,1}, {1,0}, }; for(int i=0;i<m;i++){ for(int j=0;j<n;j++){ // 为了防止走回头路,对于每个方格我们只往右或者往下合并 for(auto& d:drcts){ int newI=i+d[0],newJ=j+d[1]; if(newI>=m||newJ>=n||grid[newI][newJ]!=grid[i][j]){ // 越界或者字母不相同 continue; } // 尝试执行合并 if(!merge(i*n+j,newI*n+newJ)/*&&getSize(i*n+j)>=4*/){ // 如果没有执行合并,说明遇到已经在并查集里的了,有环 // 这个时候判断,如果集合中有 >=4 个元素就可以结束 // 其实在不走回头路的前提下,只要有环,必然路径长度 >=4 return true; } } } } return false; } }; 网友解答:


--【壹】--:

试图BFS发现情况不对,回归DFS怀抱。

class Solution { public: bool containsCycle(vector<vector<char>>& grid) { int n = grid.size(),m = grid[0].size(); std::vector<std::vector<int>> st(n,std::vector<int>(m,0)); int dx[] = {0,1,0,-1},dy[] = {1,0,-1,0}; bool res = false; auto find = [&](this auto&& find, int x,int y,int v) -> bool { st[x][y] = v; for(int i = 0; i < 4; i++) { int nx = x + dx[i],ny = y + dy[i]; if(nx < 0 || nx >= n || ny < 0 || ny >= m || grid[nx][ny] != grid[x][y]) { continue; } if(st[nx][ny]) { if(st[x][y] - st[nx][ny] >= 3) { return true; } } else { res |= find(nx,ny,v + 1); } } return false; }; for(int i = 0; i < n; i++) { for(int j = 0; j < m; j++) { if(!st[i][j]) { res |= find(i,j,1); if(res) { break; } } } } return res; } };


--【贰】--:

neighbor :: (Int, Int) -> Int -> [Int] -- m * n grid neighbor (m, n) x = let (r, c) = x `divMod` n candidates = [(r - 1, c), (r + 1, c), (r, c - 1), (r, c + 1)] in mapMaybe (toIdx (m, n)) candidates toIdx :: (Int, Int) -> (Int, Int) -> Maybe Int toIdx (m, n) (r, c) = guard (0 <= r && r < m && 0 <= c && c < n) $> (r * n + c) containsCycle :: [[Char]] -> Bool containsCycle grid = isLeft $ runExcept (evalStateT check mempty) where m = length grid n = length $ head $ fromList grid cs = UV.fromList $ concat grid dfs c curr parent = do modify (IS.insert curr) let adjs = filter (\a -> a /= parent && cs UV.! a == c) $ neighbor (m, n) curr for_ adjs $ \adj -> ifM (gets (IS.member adj)) (throwError ()) $ dfs c adj curr check :: StateT IntSet (Except ()) () check = for_ [0 .. m * n - 1] $ \i -> unlessM (gets (IS.member i)) $ dfs (cs UV.! i) i (-1)

直接dfs、遇到走过的点就说明有环,要记的状态太多而且需要callcc(throwError ())就拿变形金刚摆烂了

type DSU s = UV.MVector s Int find :: DSU s -> Int -> ST s Int find dsu x = do p <- UMV.unsafeRead dsu x if x /= p then do r <- find dsu p UMV.unsafeWrite dsu x r $> r else pure p union :: DSU s -> Int -> Int -> ST s () union dsu x y = do px <- find dsu x py <- find dsu y when (px /= py) (UMV.unsafeWrite dsu px py) isUnited :: DSU s -> Int -> Int -> ST s Bool isUnited = on (liftA2 (==)) . find containsCycle' :: [[Char]] -> Bool containsCycle' grid = isLeft $ runST $ runExceptT do let m = length grid n = length $ head $ fromList grid cs = UV.fromList $ concat grid dsu <- lift $ UMV.generate (m * n) id for_ [0 .. m * n - 1] $ \i -> do let adjs = filter (\a -> a < i && cs UV.! a == cs UV.! i) $ neighbor (m, n) i for_ adjs $ \a -> do ifM (lift $ isUnited dsu a i) (throwError ()) (lift $ union dsu a i)

拿并查集写就比较简单了,注意连的时候固定连比自己小的来避重就行了