bfs判断无向图里有没有环_leetcode第33双周赛第四题leetcode1559. 二维网格图中探测环...

leetcode1559. 二维网格图中探测环

给你一个二维字符网格数组 grid ,大小为 m x n ,你需要检查 grid 中是否存在 相同值 形成的环。

一个环是一条开始和结束于同一个格子的长度 大于等于 4 的路径。对于一个给定的格子,你可以移动到它上、下、左、右四个方向相邻的格子之一,可以移动的前提是这两个格子有 相同的值

同时,你也不能回到上一次移动时所在的格子。比方说,环 (1, 1) -> (1, 2) -> (1, 1) 是不合法的,因为从 (1, 2) 移动到 (1, 1) 回到了上一次移动时的格子。

如果 grid 中有相同值形成的环,请你返回 true ,否则返回 false

示例 1:

bfs判断无向图里有没有环_leetcode第33双周赛第四题leetcode1559. 二维网格图中探测环..._第1张图片
输入:grid = [["a","a","a","a"],["a","b","b","a"],["a","b","b","a"],["a","a","a","a"]]
输出:true
解释:如下图所示,有 2 个用不同颜色标出来的环:

bfs判断无向图里有没有环_leetcode第33双周赛第四题leetcode1559. 二维网格图中探测环..._第2张图片

示例 2:

bfs判断无向图里有没有环_leetcode第33双周赛第四题leetcode1559. 二维网格图中探测环..._第3张图片
输入:grid = [["c","c","c","a"],["c","d","c","c"],["c","c","e","c"],["f","c","c","c"]]
输出:true
解释:如下图所示,只有高亮所示的一个合法环:

bfs判断无向图里有没有环_leetcode第33双周赛第四题leetcode1559. 二维网格图中探测环..._第4张图片

示例 3:

bfs判断无向图里有没有环_leetcode第33双周赛第四题leetcode1559. 二维网格图中探测环..._第5张图片
输入: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 只包含小写英文字母。

方法:并查集

思路:

本题的做法有很多,常见的是DFS和BFS,这里介绍使用并查集的方法。

无论是哪种方法,我们都是将这个grid数组当作图来处理,即两个相邻的,且字母相同的点之间有一条无向路径。

对于并查集来说,如果两个点之间有路径,那么这两个点就应该进行合并。我们对数组中的某个点,将它与上面和左面两个相邻的点进行比较,如果字母相同,则将两点相连。之所以不选择对四个方向的相邻点进行合并,是因为如果对上下左右四个点进行比较,那么在遍历到下一个点的时候(比如该点右边的点),该点的左边点即为原来的点,导致重复;对于其他三个方向也构成重复,因此我们只选择两个方向的相邻点,这样每两个相邻点之间都只会处理一次。

下面我们考虑如何判断是环,对于某个点,我们与上面和左面的两个点进行处理,假设这两个中的某个点与该点字母相同,即需要合并,如果进行合并操作时候,发现这两个点的根节点相同,即已经在同一个连通分量了,那么就说明产生了环(因为刚开始每个点都是单独的根节点,通过不断的合并,这两个点都合并到了一个根节点下,再次遍历这个点,肯定产生了环)。

因此,我们构建长度为m * n的并查集,遍历数组中每个点,对它上面和左面两个相邻点进行操作,如果两者字母相等,且不再同一根节点,那么二者合并到一个根节点下;如果二者已经是同一个根节点了,说明遇到了环,直接返回True。遍历结束没有返回的话,返回False,说明没有环,详情见代码。

值得注意的是,二维数组中对应的点,在并查集中为一维数组,因此需要进行转换,对于点(i,j),它在并查集中的位置为i*n+j

代码:

Python3:

class UnionFind:
    #初始化,一共n个节点的并查集
    def __init__(self,n):
        self.parent = [k for k in range(n)]
    #查找某个元素的根节点
    def find(self,index):
        if self.parent[index] == index:
            return index
        #递归进行路径压缩
        self.parent[index] = self.find(self.parent[index])
        return self.parent[index]
    #合并两个下标对应的“森林”
    def union(self,index1,index2):
        self.parent[self.find(index2)] = self.find(index1)

    # 针对本题定义的函数,对两个grid值相同的结点,找它们的根节点,如果不相等,合并
    # 返回True,表示合并成功,如果两者已经在一个根下面了,说明找到环了,返回False表示合并失败
    def findandunion(self,a,b):
        x = self.find(a)
        y = self.find(b)
        if (x!=y):
            self.union(a,b)
            return True
        return False

class Solution:
    def containsCycle(self, grid: List[List[str]]) -> bool:
        m = len(grid)
        n = len(grid[0])
        # 初始化并查集
        UF = UnionFind(m*n)
        # 遍历所有点,为了避免重复,只考虑每个点向上和向左连接合并
        for i in range(m):
            for j in range(n):
                # 向左合并,第一列没有向左合并,且与左边的值相等才能合并
                if j > 0 and grid[i][j] == grid[i][j-1]:
                    # 合并失败说明遇到环了,直接返回True
                    if not UF.findandunion(i*n+j , i*n+j-1):
                        return True
                # 向上合并,第一行没有过,与上面的值相等才能合并
                if i > 0 and grid[i][j] == grid[i-1][j]:
                    # 合并失败说明遇到环了,直接返回True
                    if not UF.findandunion(i*n+j , (i-1)*n+j):
                        return True
        return False

cpp:

class UnionFind {
      
   public:
    vector parent;
    UnionFind(int n) {
      
        // 集合的代表元素 parent 数组
        parent.resize(n);
        // 初始时每个集合的代表元素就是自身
        for (int i = 0; i < n; ++i) {
      
            parent[i] = i;
        }
    }
    /* 查找 x 所在集合的代表元素,即父节点 */
    int Find(int x) {
      
        if (x != parent[x]) {
      
            // 非集合代表元素,在递归调用返回的时候,将沿途经过的结点指向根节点
            parent[x] = Find(parent[x]);
        }
        return parent[x];
    }
    /* 合并 x y 所在集合 */
    void Union(int x, int y) {
      
        // 先查找 x y 所在集合的代表元素
        int px = Find(x), py = Find(y);
        if (px != py) {
      
            // 不在同一个集合,将 x 所在集合合并到 y 所在集合
            parent[px] = py;
        }
    }
    bool findandunion(int x,int y){
      
        auto a = Find(x),b = Find(y);
        if (a != b) {
      
            Union(x,y);
            return true;
        }
        return false;
    }
};
class Solution {
      
public:
    bool containsCycle(vector>& 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 (j > 0 && grid[i][j] == grid[i][j-1]){
      
                    if (!uf.findandunion(i*n + j,i*n+j-1))
                        return true;
                }
                if (i > 0 && grid[i][j] == grid[i-1][j]){
      
                    if (!uf.findandunion(i*n + j,(i-1)*n+j))
                        return true;
                }
            }
        return false;

    }
};

结果:

bfs判断无向图里有没有环_leetcode第33双周赛第四题leetcode1559. 二维网格图中探测环..._第6张图片

bfs判断无向图里有没有环_leetcode第33双周赛第四题leetcode1559. 二维网格图中探测环..._第7张图片

你可能感兴趣的:(bfs判断无向图里有没有环)