Leet Code题解 - 1559. Detect Cycles in 2D Grid 检测二维无向图中的环


前言

找找感觉,记录一下个人解题思路系列。记录一下力扣里面的经典题目的个人想法。


一、题目描述

https://leetcode.com/problems/detect-cycles-in-2d-grid/
在二维无向图中检测环。如果有多个环,只要判断任意一个。

给定一个m*n的二维字符数组,请找出该数组中,是否存在由相同值构成的环。
一个环应该是一个长度不少于4的路径,该路径的起点和终点都在同一格内。
对任意给定格子来说,我们只能移动到其相邻格(仅限上、下、左、右四个方向,不包括斜角方向),同时,该相邻格应具有同当前格相同的值。
同时,你不能移动到你上一次曾移动过的各自中,例如:(1, 1) -> (1, 2) -> (1,1)不属于有效的环,因为对于(1, 2)来说,(1, 1)是上次访问过的格。
若存在相同值构成的环,则返回true,否则返回false。

1. 例一
例一
输入: grid = [[“a”,“a”,“a”,“a”],[“a”,“b”,“b”,“a”],[“a”,“b”,“b”,“a”],[“a”,“a”,“a”,“a”]]
输出: true
解释: 如下图,有两个环。
在这里插入图片描述
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
格内仅包含小写字母。

二、思路整理

1. 审题

可以看到,给的判断函数的原型是:bool containsCycle(vector<vector>& grid) {}
其中,输入的可以看做是一个二维的数组,数组内填充的是小写字母。
输出要求的只是判断是否存在环的结果。
一般环的判断,我们是基于联通图来做的。所以,这个二维的数组我们先需要把它转为连通图。那么整题的步骤分为:

  1. 将二维数组处理为连通图
  2. 判断连通图中是否有环

2. 分布实现步骤

2.1 将二维数组处理为连通图

由于原数组是二维数组,为了简化我们的处理逻辑,我们可以将二维的图处理为一维图。
我们可以采用一维链表来存储,并根据原图内数据(字母)处理其连通性,若:
m == grid.length
n == grid[i].length
i, j为原二维数组下标
新一维数组下标 index = i * n + j;

如下图,原表示为:
vector<vector> grid1 =
{

    {'a','a','a','a'},
    {'a','b','b','a'},
    {'a','b','b','a'},
    {'a','a','a','a'}
    
};

在这里插入图片描述

处理后的连通图为
0 : 1, 4;
1 : 0, 2;
2 : 1, 3;
3 : 2, 7;
4 : 0, 8;
5 : 9, 6;
6 : 5, 10;
7 : 3, 11;
8 : 4, 12;
9 : 5, 10;
10 : 6, 9;
11 : 7, 15;
12 : 8, 13;
13 : 12, 14;
14 : 13, 15;
15 : 11, 14;

2.2 判断连通图中是否有环

得到连通图后,本题即转换为基于一个无向图判断有环的问题。
判断图中的环,我们可以采用 DFS,深度优先检索来判断是否有环。
深度优先的通常写法是递归型的,即对现有的节点进行判断,并遍历其子节点,然后依次对子节点进行深度优先搜索。

遍历上述连通图,在遍历的过程中,我们只需判断某节点是否有一条边指向已访问过的节点,如果有,则表示存在环。但需注意:

  • 判断已访问过的节点,不应该是当前节点的父节点(无向图两个节点间是互相可联通的,但这种父子节点间的连通性不被认为是环);
  • 一个节点有三种状态,因此我们的判断应该是,遍历过程中如果发现某节点存在一条边指向非父节点的已被访问的灰色节点,则存在环;
    • 已遍历完全(该节点所有的路径(或子节点)均已被遍历) —— 黑色
    • 已被访问但未遍历完全 —— 灰色
    • 未被访问 —— 白色
  • 由于遍历是递归的,因此在某一次的子递归函数中存在环,则需要向上传递该判定结果

三、实现样例

class Solution {
private:
    bool dfs(vector<vector<int>> &connected_graph, int &start_node){
        bool has_cycle = false;

        // flag the node has been visited
        reached[start_node] = 1;
        for (auto child : connected_graph[start_node]) {
            if (reached[child] == 1 && child != parent_node[start_node]) {
                has_cycle = true;
                break;
            }
            else if (reached[child] == 0) {
                parent_node[child] = start_node;
                // pass the has_cycle result to upper layer
                has_cycle = dfs(connected_graph, child);
                if (has_cycle == true)
                {
                    break;
                }
            }
        }
        // all the path is visited
        reached[start_node] = 2;
        return has_cycle;
    }

    // represent the all the nodes can be reached to this node
    vector<int> reached;
    vector<int> parent_node;

public:
    bool containsCycle(vector<vector<char>>& grid) {

        vector<vector<int>> connected_graph;

        /******************************************/
        /* Step 1. initialize the connected graph */
        /******************************************/
        connected_graph.resize(grid.size()*grid[0].size());
        reached.resize(grid.size()*grid[0].size());
        parent_node.resize(grid.size()*grid[0].size());
        for(auto var : parent_node)
        {
            var = grid.size()*grid[0].size();
        }
        int i = 0; 
        int j = 0;
        for(i=0; i<grid.size(); i++)
        {
            for(j=0; j<grid[0].size(); j++)
            {
                // calculator how many ways each block can access
                if(i>0)
                {
                    if(grid[i][j] == grid[i-1][j])
                    {
                        connected_graph[i*grid[0].size()+j].push_back((i-1)*grid[0].size()+j);
                    }
                }
                if(j>0)
                {
                    if(grid[i][j] == grid[i][j-1])
                    {
                        connected_graph[i*grid[0].size()+j].push_back(i*grid[0].size()+j-1);
                    }
                }
                if(i<grid.size()-1)
                {
                    if(grid[i][j] == grid[i+1][j])
                    {
                        connected_graph[i*grid[0].size()+j].push_back((i+1)*grid[0].size()+j);
                    }
                }
                if(j<grid[0].size()-1)
                {
                    if(grid[i][j] == grid[i][j+1])
                    {
                        connected_graph[i*grid[0].size()+j].push_back(i*grid[0].size()+j+1);
                    }
                }
            }
        }

        /******************************************/
        /* Step 2. judge there has cycle          */
        /******************************************/
        bool has_cycle = false;

        for (int k = 0; k < reached.size(); k++)
        {
            /* begin the dfs search beginning node */
            if(reached[k] == 0)
            {
                // bfs search from node-k
                has_cycle = dfs(connected_graph, k);
            }
            else
            {
                continue;
            }

            if(has_cycle == true){
                return true;
            }
        }

        return has_cycle;        
    }
};
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
探险家小扣的行动轨迹,都将保存在记录仪。expeditions[i] 表示小扣第 i 次探险记录,用一个字符串数组表示。其的每个「营地」由大小写字母组成,通过子串 -> 连接。例:"Leet->code->Campsite",表示到访了 "Leet"、"code"、"Campsite" 三个营地。expeditions[0] 包含了初始小扣已知的所有营地;对于之后的第 i 次探险(即 expeditions[i] 且 i > 0),如果记录包含了之前均没出现的营地,则表示小扣 新发现 的营地。 请你找出小扣发现新营地最多且索引最小的那次探险,并返回对应的记录索引。如果所有探险记录都没有发现新的营地,返回 -1。注意: 大小写不同的营地视为不同的营地; 营地的名称长度均大于 0。用python实现。给你几个例子:示例 1: 输入:expeditions = ["leet->code","leet->code->Campsite->Leet","leet->code->leet->courier"] 输出:1 解释: 初始已知的所有营地为 "leet" 和 "code" 第 1 次,到访了 "leet"、"code"、"Campsite"、"Leet",新发现营地 2 处:"Campsite"、"Leet" 第 2 次,到访了 "leet"、"code"、"courier",新发现营地 1 处:"courier" 第 1 次探险发现的新营地数量最多,因此返回 1。示例 2: 输入:expeditions = ["Alice->Dex","","Dex"] 输出:-1 解释: 初始已知的所有营地为 "Alice" 和 "Dex" 第 1 次,未到访任何营地; 第 2 次,到访了 "Dex",未新发现营地; 因为两次探险均未发现新的营地,返回 -1
最新发布
04-23

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值