列表

详情


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

 

提示:

原站题解

去查看

上次编辑到这里,代码来自缓存 点击恢复默认模板
class Solution { public: bool containsCycle(vector<vector<char>>& 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;
    }
}

上一题