算法|图论 3

news/2024/7/20 20:29:34 标签: 算法, 图论, 深度优先

LeetCode 130- 被围绕的区域

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

题目描述:给你一个 m x n 的矩阵 board ,由若干字符 'X' 和 'O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。

解题思路(思路一是自己的思路,思路二题解思路,不过用两种遍历方式写出来)

思路一(深度优先遍历):

我的思路:是开辟一个二维数组,存储二元组,来记录那些下标,首先是一个一维数组,每次记录我们当前遍历岛屿的下标,当被环绕的时候压入到二维数组中,否则直接清空。类似将所有岛屿都淹没,然后再看哪些岛屿没被包围再还原成陆地。

  1. 首先确定递归函数的参数,返回值。覆盖那些被环绕的区域,我们直接设置两个全局变量vec(存储所有需要还原的海洋)和tmp(存储当前我们遍历的这片岛屿的下标),flag表示是否被包围,这样可以不用写太多参数传递。返回值就是void,参数需要图和一个x和y来记录当前在哪个岛屿。下次从这个岛屿开始走的。
  2. 确定终止条件,本题按我们的逻辑,先进入循环再判断是否应该终止,那么终止条件就是:当超出网格范围(这里还需要把flag 设置为false,表示没被包围) 或 该岛屿已经是海洋 就终止。
  3. 单层处理逻辑,每次将当前岛屿淹没(这里也就是设置为'X'),然后将其下标压入tmp(为什么有tmp,是因为我们不知道当前的这片岛屿马上是否要恢复,也就是变成'O'所以需要一个临时的,当我们确定要恢复的时候就将其压入到vec中)。
class Solution {
public:
    bool flag = true; //表示是否被环绕(只要相连的岛屿有一个在边界就不是被环绕的),被环绕就不用撤回修改
    vector<vector<pair<int,int>>> vec;//存所有岛屿下标,其中全都是要变回'O'的
    vector<pair<int,int>> tmp; //保存当前遍历这片岛屿的下标,一会可能填充。
    void dfs(vector<vector<char>> &grid,int x,int y){
        if(x < 0 || y < 0 || x >= grid.size() || y >= grid[0].size()){
            flag = false;//表示没有被环绕
            return ;
        }
        if(grid[x][y] == 'X') return;
        tmp.push_back({x,y});//压入当前岛屿的下标
        grid[x][y] = 'X';
        dfs(grid,x+1,y);
        dfs(grid,x-1,y);
        dfs(grid,x,y+1);
        dfs(grid,x,y-1);
    }
    void solve(vector<vector<char>>& grid) {
        int m = grid.size(),n = grid[0].size();
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(grid[i][j] == 'O'){
                    dfs(grid,i,j);
                    //当flag为false,也就是岛屿没有被包围,我们就将其压入vec中,最后再还原
                    if(flag == false){
                        vec.push_back(tmp);
                        tmp.clear();//这里注意要把tmp清空了,方便下次使用
                        flag = true;//flag再改为默认值
                    }else{
                        //这里不改flag是因为如果走这个分支,那flag一定还是默认值true
                        tmp.clear();
                    }
                }
            }
        }
        //将未被包围的小岛屿还原为'O'
        for(int i=0;i<vec.size();i++){
            for(int j=0;j<vec[i].size();j++){
                grid[vec[i][j].first][vec[i][j].second] = 'O';
            }
        }
        return ;
    }
};

思路二(广度优先遍历):

比较猛的一个思路:本题我们可以看到,只有和边界相连的O不会变,其余的都得变成X,那么我们可以将与边界相连的所有O全部变成一个特定符号如'-',这样,其中所有的'-'都是与边界相连的,马上会再变成'O'。而这个时候图中所有的'O'都是被包围的,全都变为'X'即可。

class Solution {
public:
    int dir[4][2] = {0,1,1,0,-1,0,0,-1};
    void bfs(vector<vector<char>> &grid,int x,int y){
        queue<pair<int,int>> que;
        que.push({x,y});
        grid[x][y] = '-';//将与边界相连的全部变为'-'
        //广度优先遍历
        while(!que.empty()){
            pair<int,int> cur = que.front();
            que.pop();
            for(int i=0;i<4;i++){
                int nextx = cur.first + dir[i][0];
                int nexty = cur.second + dir[i][1];
                if(nextx < 0 || nexty < 0 || nextx >= grid.size() || nexty >= grid[0].size() ||grid[nextx][nexty] != 'O') continue;
                que.push({nextx,nexty});
                grid[nextx][nexty] = '-';
            }

        }
    }
    void solve(vector<vector<char>>& grid) {
        int row = grid.size(),col = grid[0].size();
        for(int i=0;i<col;i++){
            //遍历第一行的所有列
            if(grid[0][i] == 'O') bfs(grid,0,i);
            //遍历最后一行的所有列
            if(grid[row-1][i] == 'O') bfs(grid,row-1,i);
        }
        for(int i=0;i<row;i++){
            //遍历第一列的所有行
            if(grid[i][0] == 'O') bfs(grid,i,0);
            //遍历最后一列的所有行
            if(grid[i][col-1] == 'O') bfs(grid,i,col-1);
        }
        //这个时候图中所有'-'都是与边界相连的,所有'O'都是被包围起来的。
        for(int i=0;i<row;i++){
            for(int j=0;j<col;j++){
                //这俩式子顺序反了,导致花了我半小时调试
                if(grid[i][j] == 'O')grid[i][j] = 'X';
                if(grid[i][j] == '-')grid[i][j] = 'O';
            }
        }
        return ;
    }
};

总结:

  • 深搜和广搜的问题,广搜里面写的那个思路很清奇,还是得多看题目的提示,可以减少一点时间和空间的损耗。

LeetCode 417- 太平洋大西洋水流问题

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

题目描述:有一个 m × n 的矩形岛屿,与 太平洋大西洋 相邻。 “太平洋” 处于大陆的左边界和上边界,而 “大西洋” 处于大陆的右边界和下边界。

这个岛被分割成一个由若干方形单元格组成的网格。给定一个 m x n 的整数矩阵 heights , heights[r][c] 表示坐标 (r, c) 上单元格 高于海平面的高度

岛上雨水较多,如果相邻单元格的高度 小于或等于 当前单元格的高度,雨水可以直接向北、南、东、西流向相邻单元格。水可以从海洋附近的任何单元格流入海洋。

返回网格坐标 result 的 2D 列表 ,其中 result[i] = [ri, ci] 表示雨水从单元格 (ri, ci) 流动 既可流向太平洋也可流向大西洋

解题思路

首先明确本题题意,意思就是雨水高的地方能往雨水低的地方流,要我们求出能流向两大洋的点坐标。

思路一(深度优先遍历):

这题总体思路就是遍历边界点(也就是能直接流进大洋的点),再逆流遍历,因为雨水往低处流,我们逆流遍历就可以找到所有大于边界的雨水高度的点,这些点就是可以直接流进大洋的点

  1. 首先确定递归函数的参数,返回值。参数我们需要设置一个isvisited来表示这个点能不能走到其中一个大洋中去。还有x,y坐标来表示当前遍历到的点的位置
  2. 确定终止条件,本题按我们的逻辑,先进入循环再判断是否应该终止,那么终止条件就是:当超出网格范围 当前遍历的是海洋 就跳过。不过这里需要注意,这道题目我们这里的逻辑是逆流而上,所以下一次要去的节点的雨水高度 小于 我们当前节点的雨水高度的时候也直接跳过。
  1. 单层处理逻辑,每次进入递归,我们就将访问设置为true,代表可以直接流进大洋。不过在处理遍历的时候有点麻烦,需要注意m和n各表示的是什么,别弄混了。
class Solution {
private:
    int dir[4][2] = {-1, 0, 0, -1, 1, 0, 0, 1}; // 保存四个方向

    // 从低向高遍历,注意这里visited是引用,即可以改变传入的pacific和atlantic的值
    void dfs(vector<vector<int>>& heights, vector<vector<bool>>& visited, int x, int y) {
        if (visited[x][y]) return;
        visited[x][y] = true;
        for (int i = 0; i < 4; i++) { // 向四个方向遍历
            int nextx = x + dir[i][0];
            int nexty = y + dir[i][1];
            // 超过边界
            if (nextx < 0 || nextx >= heights.size() || nexty < 0 || nexty >= heights[0].size()) continue;
            // 高度不合适,注意这里是从低向高判断
            if (heights[x][y] > heights[nextx][nexty]) continue;

            dfs (heights, visited, nextx, nexty);
        }
        return;
    }
public:

    vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {
        vector<vector<int>> result;
        int n = heights.size();
        int m = heights[0].size(); // 这里不用担心空指针,题目要求说了长宽都大于1

        // 记录从太平洋边出发,可以遍历的节点
        vector<vector<bool>> pacific = vector<vector<bool>>(n, vector<bool>(m, false));

        // 记录从大西洋出发,可以遍历的节点
        vector<vector<bool>> atlantic = vector<vector<bool>>(n, vector<bool>(m, false));

        // 从最上最下行的节点出发,向高处遍历
        for (int i = 0; i < n; i++) {
            dfs (heights, pacific, i, 0); // 遍历最上行,接触太平洋
            dfs (heights, atlantic, i, m - 1); // 遍历最下行,接触大西洋
        }

        // 从最左最右列的节点出发,向高处遍历
        for (int j = 0; j < m; j++) {
            dfs (heights, pacific, 0, j); // 遍历最左列,接触太平洋
            dfs (heights, atlantic, n - 1, j); // 遍历最右列,接触大西洋
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                // 如果这个节点,从太平洋和大西洋出发都遍历过,就是结果
                if (pacific[i][j] && atlantic[i][j]) result.push_back({i, j});
            }
        }
        return result;
    }
};

总结:

  • 逆流而上的操作,主要是根据边界来想到了这个思路。

http://www.niftyadmin.cn/n/5028303.html

相关文章

C 语言中关键字const

const 是 C 语言中的一个关键字&#xff0c;它表示一个对象或变量是常量&#xff0c;即在其生命周期内不可更改。在 C 语言中&#xff0c;const 有多种用法&#xff0c;可以提高代码的可读性和安全性。这里列举了一些关于 const 的常见用法&#xff1a; 声明常量变量&#xff…

反渗透,sql注入漏洞扫描工具

工具一:zap 下载地址&#xff1a;ZAP GitHub OWASP Zed 攻击代理&#xff08;ZAP&#xff09;是世界上最受欢迎的免费安全审计工具之一&#xff0c;由数百名国际志愿者积极维护。它可以帮助你在开发和测试应用程序时自动查找 Web 应用程序中的安全漏洞。可以说 ZAP 是一个中…

全量数据采集:不同网站的方法与挑战

简介 在当今数字化时代中&#xff0c;有数据就能方便我们做出很多决策。数据的获取与分析已经成为学术研究、商业分析、战略决策以及个人好奇心的关键驱动力。本文将分享不同网站的全量数据采集方法&#xff0c;以及在这一过程中可能会遇到的挑战。 部分全量采集方法 1. 撞店…

爬虫 — Xpath 数据解析

目录 一、介绍二、使用三、语法1、//2、/3、4、/text5、[]、[] 四、练习1、元组写入2、对象写入 五、豆瓣电影信息爬取 一、介绍 XPath&#xff08;XML Path Language&#xff09;是一种 XML 的查询语言&#xff0c;它能在 XML 树状结构中寻找节点。XPath 用于在 XML 文档中通…

#循循渐进学51单片机#如何学习单片机#not.1

1、了解普通发光二极管的参数&#xff0c;掌握限流电阻的计算方法。 1&#xff09; LED小灯靠电流点亮&#xff0c;电压1.8v~2.2v&#xff0c;电流是1~20ma&#xff0c;在1~5ma亮度有所变化&#xff0c;5MA以上亮度不变。 2&#xff09; 限流电阻的算法一般采用欧姆定律计算。…

SRT一个简单的客户端和服务端

1.客户端 支持将UDP数据流接收后进行SRT流的推送&#xff0c;也支持从服务端拉取SRT流&#xff0c;同时支持SRT会话模式的测试。项目依赖于msprotocol: 一个轻量级的网络协议&#xff0c;扩展方便使用简单。可应用于X86和ARM64嵌入式设备&#xff0c;目前已支持file,hls,http,r…

偶现来电时手机操作出现重启

问题描述&#xff1a;偶现来电时手机操作出现重启 问题分析&#xff1a;从系统Log看 09-06 10:22:44.791829 1400 1425 W Watchdog: *** WATCHDOG KILLING SYSTEM PROCESS: Blocked in handler on main thread (main) 09-06 10:22:44.794133 1400 1425 W Watchdog: main …

SpringMVC之自定义注解

目录 一.JAVA注解简介 1.1.Java注解分类 1.2.JDK元注解 二.自定义注解 1.1.如何自定义注解 1.2.自定义注解的基本案例 1.2.1.案例一&#xff08;获取类与方法上的注解值&#xff09; 1.2.2.案例二&#xff08;获取类属性上的注解属性值&#xff09; 1.2.3. 案例三&#xff…