Day66|图part5:130. 被围绕的区域、827.最大人工岛

2023-09-18 10:48:47

130. 被围绕的区域

leetcode链接:题目链接

image

这题看起来很复杂,其实跟之前找飞地和找边缘地区的是差不多的,主要分三步:

  • 使用dfs将边缘的岛都找出来,然后用A代替防止混淆;
  • 再用dfs找中间不与任何岛相连的飞地;
  • 最后把之前的A替换成O。

最终代码:

class Solution {
public:
    void dfs(vector<vector<char>> &board, int i, int j, char ch){//找到o把其周围转化为ch
        vector<int> dir = {0,-1,0,1,1,0,-1,0};
        if(i < 0 || j < 0 || i >= board.size() || j >= board[0].size()){
            return;
        }
        if(board[i][j] == 'X' || board[i][j] == 'a'){
            return;
        }
        board[i][j] = ch;
        for(int idx = 0; idx < 4; idx++){
            dfs(board, i + dir[2 * idx], j + dir[2 * idx + 1],ch);
        }

    }
    void solve(vector<vector<char>>& board) {
        int m = board.size();
        int n = board[0].size();

        for(int i = 0; i < m; i++){
            if(board[i][n - 1] == 'O'){
                dfs(board,i, n - 1, 'a');
            }
            if(board[i][0] == 'O'){
                dfs(board,i,0, 'a');
            }
        }
        for(int j = 0; j < n; j++){
            if(board[0][j] == 'O'){
                dfs(board,0, j,'a');
            }
            if(board[m - 1][j] == 'O'){
                dfs(board, m - 1, j,'a');
            }

        }
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n;j++){
                if(board[i][j] == 'O'){
                    board[i][j] = 'X';
                }
                if(board[i][j] =='a'){
                    board[i][j] = 'O';
                }

            }
        }
//        for(int i = 0; i < m; i++){
//            for(int j = 0; j < n;j++){
//                if(board[i][j] =='a'){
//                    board[i][j] = 'O';
//                }
//            }
//        }
    }
};

这里有几点注意的:

  • dfs函数中,除了等于X需要return,等于A也需要return,否则递归就没办法结束。
  • 只需要dfs淹没周围一圈的岛,中间的岛不用再dfs找了,直接替换就行,因为本题没有让我们以整个岛为单位做一些事情。

417. 太平洋大西洋水流问题

leetcode链接:题目链接

image

image

输入: heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]]
输出: [[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]
示例 2:

输入: heights = [[2,1],[1,2]]
输出: [[0,0],[0,1],[1,0],[1,1]]

首先这题题目我都没怎么看懂,理解一下题意。

输入是一个二维数组,只要位于该点的值比四个方向的高就可以流向其他方向,最终既可以流向右边、下边(大西洋),又可以流向左边、上边(太平洋)的符合结果,输出result。

首先能想到一个很朴素的思路就是,对每个点都判断一下是否能同时流向大西洋和太平洋。这种思路的实现代码如下:

class Solution {
private:
    int dir[4][2] = {-1, 0, 0, -1, 1, 0, 0, 1};
    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;
    }
  bool isResult(vector<vector<int>>& heights, int x, int y) {
        vector<vector<bool>> visited = vector<vector<bool>>(heights.size(), vector<bool>(heights[0].size(), false));

        // 深搜,将x,y出发 能到的节点都标记上。 
        dfs(heights, visited, x, y);
        bool isPacific = false;
        bool isAtlantic = false;

        // 以下就是判断x,y出发,是否到达太平洋和大西洋 
        for (int j = 0; j < heights[0].size(); j++) {
            if (visited[0][j]) {
                isPacific = true;
                break;
            }
        }
        for (int i = 0; i < heights.size(); i++) {
            if (visited[i][0]) {
                isPacific = true;
                break;
            }
        }
        for (int j = 0; j < heights[0].size(); j++) {
            if (visited[heights.size() - 1][j]) {
                isAtlantic = true;
                break;
            }
        }
        for (int i = 0; i < heights.size(); i++) {
            if (visited[i][heights[0].size() - 1]) {
                isAtlantic = true;
                break;
            }
        }
        if (isAtlantic && isPacific) return true;
        return false;
    }
public:

    vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {
        vector<vector<int>> result;
        // 遍历每一个点,看是否能同时到达太平洋和大西洋 
        for (int i = 0; i < heights.size(); i++) {
            for (int j = 0; j < heights[0].size(); j++) {
                if (isResult(heights, i, j)) result.push_back({i, j});
            }
        }
        return result;
    }
};

这种思路很直白,但很明显,以上代码超时了。 来看看时间复杂度。

遍历每一个节点,是 m * n,遍历每一个节点的时候,都要做深搜,深搜的时间复杂度是: m * n

那么整体时间复杂度 就是 O(m^2 * n^2) ,这是一个四次方的时间复杂度。

优化

那么我们可以 反过来想,从太平洋边上的节点 逆流而上,将遍历过的节点都标记上。 从大西洋的边上节点 逆流而长,将遍历过的节点也标记上。 然后两方都标记过的节点就是既可以流太平洋也可以流大西洋的节点。

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;
    }
};

所以 调用dfs函数,只要参数传入的是 数组pacific,那么地图中 每一个节点其实就遍历一次,无论你调用多少次

同理,调用 dfs函数,只要 参数传入的是 数组atlantic,地图中每个节点也只会遍历一次。

所以,以下这段代码的时间复杂度是 2 * n * m。 地图用每个节点就遍历了两次,参数传入pacific的时候遍历一次,参数传入atlantic的时候遍历一次。

827. 最大人工岛

leetcode链接:力扣链接

给你一个大小为 n x n 二进制矩阵 grid 。最多 只能将一格 0 变成 1 。

返回执行此操作后,grid 中最大的岛屿面积是多少?

岛屿 由一组上、下、左、右四个方向相连的 1 形成。

示例 1:

输入: grid = [[1, 0], [0, 1]]
输出: 3
解释: 将一格0变成1,最终连通两个小岛得到面积为 3 的岛屿。
示例 2:

输入: grid = [[1, 1], [1, 0]]
输出: 4
解释: 将一格0变成1,岛屿的面积扩大为 4。
示例 3:

输入: grid = [[1, 1], [1, 1]]
输出: 4
解释: 没有0可以让我们变成1,面积依然为 4

https://www.programmercarl.com/0827.%E6%9C%80%E5%A4%A7%E4%BA%BA%E5%B7%A5%E5%B2%9B.html#%E4%BC%98%E5%8C%96%E6%80%9D%E8%B7%AF

先放这里放一下。题目居然是hard,不太想做岛屿类的题了,换换脑子。

总结

  • 岛屿问题的dfs/bfs的代码基本大差不差,主要还是主函数中处理逻辑的问题;
  • 学习带有 visited的dfs的写法。
更多推荐

GaussDB技术解读系列:数据库迁移创新实践

近日,以“数智赋能共筑未来”为主题的第14届中国数据库技术大会(DTCC2023)在北京举行,在GaussDB“五高两易”核心技术,给世界一个更优选择的专场,华为云数据库生态工具研发总监窦德明分享了GaussDB数据库的迁移创新实践。本篇将分享GaussDB数据库迁移的创新实践。易迁移能力是企业数据库替换选型的关键考量

华硕电脑怎么录屏?分享实用录制经验!

“华硕电脑怎么录屏呀,刚买的笔记本电脑,是华硕的,自我感觉挺好用的,但是不知道怎么录屏,最近刚好要录一个教程,怎么都找不到在哪里录制,有人能教教我吗?”随着电脑技术的不断发展,人们越来越依赖电脑进行工作和娱乐。录屏功能作为电脑的一项重要功能,已经逐渐成为人们日常生活中的必备工具。华硕作为知名的电脑品牌,为用户提供了便捷

C语言——自定义类型结构体_学习笔记

结构体的基本概念结构体是一种用户自定义的数据类型,可以包含多个不同类型的变量。通过使用结构体,我们可以将相关联的数据组织在一起,便于管理和使用。结构体的声明正常的结构体声明在C语言中,结构体(struct)指的是一种数据结构,是C语言中聚合数据类型的一类。结构体可以包含多个不同类型的数据成员,例如:int、float、

linux的应用线程同步与驱动同步机制

同步机制在Linux应用程序和内核中的驱动程序中,有一些常见的同步机制用于实现线程或进程之间的同步和数据访问保护。下面是它们的一些主要机制:Linux应用程序中的同步机制:互斥锁(Mutex):用于保护共享资源,确保只有一个线程可以访问该资源。应用程序可以使用pthread_mutex_t类型的互斥锁,使用pthrea

爬虫入门基础:深入解析HTTP协议的工作过程

目录一、HTTP协议简介二、HTTP协议的工作过程三、请求方法与常见用途四、请求头与常见字段五、状态码与常见含义六、进阶话题和注意事项总结在如今这个数字化时代,互联网已经成为我们获取信息、交流和娱乐的主要渠道。而在互联网中,HTTP协议则扮演着至关重要的角色。HTTP,全称HypertextTransferProtoc

SpringMVC之JSR303和拦截器

目录一.JSR303二.JSR常用的注解三.JSR快速入门四.拦截器⭐⭐⭐拦截器和过滤器有什么不一样,或者它们的区别是什么??五.拦截器快速入门--登录的案例一.JSR303JSR303是Java规范的一部分,全称为BeanValidation框架。它定义了一组基于标注的验证注解,用于验证Java对象的属性值的合法性。

【Java 基础篇】ThreadPoolExecutor 详解

多线程编程是现代应用程序开发中的一个重要主题。为了更有效地管理和利用多线程资源,Java提供了丰富的线程池支持。ThreadPoolExecutor类是Java中用于创建和管理线程池的核心类之一,本文将详细介绍ThreadPoolExecutor的使用方法和原理。线程池的基本概念在深入探讨ThreadPoolExecu

leetcode 54. 螺旋矩阵

1.题解如果一条边从头遍历到底,则下一条边遍历的起点随之变化选择不遍历到底,可以减小横向、竖向遍历之间的影响一轮迭代结束时,4条边的两端同时收窄1一轮迭代所做的事情变得很清晰:遍历一个“圈”,遍历的范围收缩为内圈一层层向里处理,按顺时针依次遍历:上、右、下、左。不再形成“环”了,就会剩下:一行或一列,然后单独判断矩阵不

计算机竞赛 机器视觉opencv答题卡识别系统

0前言🔥优质竞赛项目系列,今天要分享的是🚩答题卡识别系统-opencvpython图像识别该项目较为新颖,适合作为竞赛课题方向,学长非常推荐!🥇学长这里给一个题目综合评分(每项满分5分)难度系数:3分工作量:3分创新点:3分🧿更多资料,项目分享:https://gitee.com/dancheng-senior

Vue路由及Node.js环境搭建

目录一.Vue路由1.1定义1.2应用领域1.3代码展示二、Node.js2.1定义2.2特点三.Node.js安装与配置好啦今天到这了,希望帮到你!!!一.Vue路由1.1定义Vue路由是指使用VueRouter插件来管理前端应用程序的导航和页面路由的过程。它允许你在单页面应用程序(SPA)中定义不同的路由路径,并将

JVM相关知识

运行程序时JVM中内存区域的划分:线程私有:本地方法栈:本地方法栈与虚拟机栈相似,区别是,本地方法栈为虚拟机使用的本地方法服务,虚拟机栈为虚拟机使用的JAVA方法服务。虚拟机栈:虚拟机栈中保存的主要是一个个栈帧,每当有一个方法被调用时,都会有栈帧入栈,方法结束时,栈帧就会被弹出,每个栈帧由局部变量表和操作数栈,动态连接

热文推荐