【算法与数据结构】1020、130、LeetCode飞地的数量 被围绕的区域

news2024/11/18 6:35:42

文章目录

  • 一、1020、飞地的数量
  • 二、130、被围绕的区域
  • 三、完整代码

所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。

一、1020、飞地的数量

在这里插入图片描述
在这里插入图片描述

  思路分析:博主认为题目很抽象,非常难理解。想了好久,要理解题目什么意思,必须理解“移动”这个概念。“移动”是指陆地可以移动,移动到连接的陆地单元或者跨过边界。例如示例1中的(1, 0)这块陆地可以移出边界,示例2中(2, 2)这块陆地,可以按照 ( 1 , 2 ) − > ( 0 , 2 ) − > ( 0 , 1 ) − > 边界外 (1, 2)->(0, 2)->(0, 1)->边界外 (1,2)>(0,2)>(0,1)>边界外 的顺序离开网格边界。其他的陆地也类似,连接的陆地都可以移出边界。另一方面,从题目来理解更简单,要求飞地的数量。所谓飞地就是不和边界挨着的陆地,这也和任意次数“移动”出网格边界的定义一致。

  飞地的数量我们一眼就能看出,不和边界挨着的就是飞地。反过来想,我们顺着边界找到所有连接的陆地,讲这些陆地全部删除,剩下的就都是飞地,然后统计数量即可。程序当中,删除的这一操作不必实际进行,我们将其标记为已遍历,只要坐标是陆地且没有被遍历过就是飞地。

  程序如下

// 1020、飞地的数量-深度优先搜索
class Solution {
private:
	int Area = 0;
	vector<vector<int>> delta_x_y = { {0, -1}, {0, 1}, {-1, 0}, {1, 0} };	// 上下左右四个方向的偏移量
	void dfs(vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y) {	// 1、递归输入参数
		// 2、终止条件 访问过或者遇到海水,又或者越界
		if (x < 0 || x >= grid.size() || y < 0 || y >= grid[0].size() || visited[x][y] || grid[x][y] == 0) return;  // 越界了,直接跳过
		visited[x][y] = true;
		//grid[x][y] = 0;		// 可以省略
		// 3、单层递归逻辑
		for (int i = 0; i < 4; i++) {
			int nextx = x + delta_x_y[i][0];
			int nexty = y + delta_x_y[i][1];			 
			dfs(grid, visited, nextx, nexty);
		}
	}
public:
    int numEnclaves(vector<vector<int>>& grid) {
		vector<vector<bool>> visited = vector<vector<bool>>(grid.size(), vector<bool>(grid[0].size(), false));	// 遍历过的坐标
		// 遍历最外面的一圈
		for (int i = 0; i < grid.size(); i++) {		// 遍历两列
			dfs(grid, visited, i, 0);
			dfs(grid, visited, i, grid[0].size() - 1);
		}
		for (int j = 1; j < grid[0].size() - 1; j++) {	// 遍历两行
			dfs(grid, visited, 0, j);
			dfs(grid, visited, grid.size() - 1, j);
		}

		for (int i = 1; i < grid.size() - 1; i++) {	// 遍历行
			for (int j = 1; j < grid[0].size() - 1; j++) {	// 遍历列
				if (grid[i][j] == 1 && !visited[i][j]) Area++;	// 深度优先搜索,将连接的陆地都标记上true
			}
		}
		return Area;
    }
};

复杂度分析:

  • 时间复杂度: O ( m × n ) O(m \times n) O(m×n),其中 m m m n n n分别是岛屿数组的行数和列数。
  • 空间复杂度: O ( m × n ) O(m \times n) O(m×n),主要是栈的调用,最坏情况下,网格全是陆地,深度优先搜索的深度达到 m × n m \times n m×n

二、130、被围绕的区域

在这里插入图片描述
在这里插入图片描述

  思路分析:本题需要求将飞地改成‘X’。那么一个思路就是沿着网格边界搜索一遍,找到所有的‘O’并标记,表示这些‘O’不是飞地。然后,再将网格数组中的所有未标记过的‘O’改成‘X’即可。按照这样的思路需要一个额外的visited数组来标记‘O’,造成额外开销。实际上我们只需要区别标记过的‘O’(挨着边界的陆地)和未标记的‘O’(飞地),将标记过的‘O’改成其他字符即可(例如‘A’或者‘B’或者其他任意一个字符)。

  程序当中,先沿着边界遍历挨着边界的陆地,都改成‘A’。然后遍历边界以外的网格点,碰见‘O’就必然是飞地,将其改成‘X’。最后再将‘A’变回‘O’。

  程序如下

// 130、被围绕的区域-深度优先搜索
class Solution2 {
private:
	vector<vector<int>> delta_x_y = { {0, -1}, {0, 1}, {-1, 0}, {1, 0} };	// 上下左右四个方向的偏移量
	void dfs(vector<vector<char>>& board, int x, int y) {	// 1、递归输入参数
		// 2、终止条件 遇到海水或者越界,遇到遍历过的陆地
		if (x < 0 || x >= board.size() || y < 0 || y >= board[0].size() || board[x][y] == 'X' || board[x][y] == 'A') return;
		board[x][y] = 'A';
		// 3、单层递归逻辑
		for (int i = 0; i < 4; i++) {
			int nextx = x + delta_x_y[i][0];
			int nexty = y + delta_x_y[i][1];
			dfs(board, nextx, nexty);
		}
	}
public:
	void solve(vector<vector<char>>& board) {
		vector<vector<bool>> visited = vector<vector<bool>>(board.size(), vector<bool>(board[0].size(), false));	// 遍历过的坐标
		// 遍历最外面的一圈,找到挨着边界的陆地
		for (int i = 0; i < board.size(); i++) {		// 遍历外圈的两列
			if (board[i][0] == 'O') dfs(board, i, 0);
			if (board[i][board[0].size() - 1] == 'O') dfs(board, i, board[0].size() - 1);
		}
		for (int j = 1; j < board[0].size() - 1; j++) {	// 遍历外圈的两行
			if (board[0][j] == 'O') dfs(board, 0, j);
			if (board[board.size() - 1][j] == 'O') dfs(board, board.size() - 1, j);
		}
		// 遍历除边界以外的格点
		for (int i = 0; i < board.size(); i++) {	// 遍历行
			for (int j = 0; j < board[0].size(); j++) {	// 遍历列
				if (board[i][j] == 'O') board[i][j] = 'X';	// 删除飞地
 				if (board[i][j] == 'A') board[i][j] = 'O';	// 还原'O'
			}
		}
	}
};

复杂度分析:

  • 时间复杂度: O ( m × n ) O(m \times n) O(m×n),其中 m m m n n n分别是网格数组的行数和列数。
  • 空间复杂度: O ( m × n ) O(m \times n) O(m×n),主要是栈的调用。最坏情况下,网格全是‘O’,深度优先搜索的深度达到 m × n m \times n m×n

三、完整代码

# include <iostream>
# include <vector>
# include <string>
using namespace std;

// 1020、飞地的数量-深度优先搜索
class Solution {
private:
	int Area = 0;
	vector<vector<int>> delta_x_y = { {0, -1}, {0, 1}, {-1, 0}, {1, 0} };	// 上下左右四个方向的偏移量
	void dfs(vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y) {	// 1、递归输入参数
		// 2、终止条件 访问过或者遇到海水,又或者越界
		if (x < 0 || x >= grid.size() || y < 0 || y >= grid[0].size() || visited[x][y] || grid[x][y] == 0) return;  // 越界了,直接跳过
		visited[x][y] = true;
		//grid[x][y] = 0;		// 可以省略
		// 3、单层递归逻辑
		for (int i = 0; i < 4; i++) {
			int nextx = x + delta_x_y[i][0];
			int nexty = y + delta_x_y[i][1];			 
			dfs(grid, visited, nextx, nexty);
		}
	}
public:
    int numEnclaves(vector<vector<int>>& grid) {
		vector<vector<bool>> visited = vector<vector<bool>>(grid.size(), vector<bool>(grid[0].size(), false));	// 遍历过的坐标
		// 遍历最外面的一圈
		for (int i = 0; i < grid.size(); i++) {		// 遍历外圈的两列
			dfs(grid, visited, i, 0);
			dfs(grid, visited, i, grid[0].size() - 1);
		}
		for (int j = 1; j < grid[0].size() - 1; j++) {	// 遍历外圈的两行
			dfs(grid, visited, 0, j);
			dfs(grid, visited, grid.size() - 1, j);
		}

		for (int i = 1; i < grid.size() - 1; i++) {	// 遍历行
			for (int j = 1; j < grid[0].size() - 1; j++) {	// 遍历列
				if (grid[i][j] == 1 && !visited[i][j]) Area++;	// 深度优先搜索,将连接的陆地都标记上true
			}
		}
		return Area;
    }
};

// 130、被围绕的区域-深度优先搜索
class Solution2 {
private:
	vector<vector<int>> delta_x_y = { {0, -1}, {0, 1}, {-1, 0}, {1, 0} };	// 上下左右四个方向的偏移量
	void dfs(vector<vector<char>>& board, int x, int y) {	// 1、递归输入参数
		// 2、终止条件 遇到海水或者越界,遇到遍历过的陆地
		if (x < 0 || x >= board.size() || y < 0 || y >= board[0].size() || board[x][y] == 'X' || board[x][y] == 'A') return;
		board[x][y] = 'A';
		// 3、单层递归逻辑
		for (int i = 0; i < 4; i++) {
			int nextx = x + delta_x_y[i][0];
			int nexty = y + delta_x_y[i][1];
			dfs(board, nextx, nexty);
		}
	}
public:
	void solve(vector<vector<char>>& board) {
		vector<vector<bool>> visited = vector<vector<bool>>(board.size(), vector<bool>(board[0].size(), false));	// 遍历过的坐标
		// 遍历最外面的一圈,找到挨着边界的陆地
		for (int i = 0; i < board.size(); i++) {		// 遍历外圈的两列
			if (board[i][0] == 'O') dfs(board, i, 0);
			if (board[i][board[0].size() - 1] == 'O') dfs(board, i, board[0].size() - 1);
		}
		for (int j = 1; j < board[0].size() - 1; j++) {	// 遍历外圈的两行
			if (board[0][j] == 'O') dfs(board, 0, j);
			if (board[board.size() - 1][j] == 'O') dfs(board, board.size() - 1, j);
		}
		// 遍历除边界以外的格点
		for (int i = 0; i < board.size(); i++) {	// 遍历行
			for (int j = 0; j < board[0].size(); j++) {	// 遍历列
				if (board[i][j] == 'O') board[i][j] = 'X';	// 删除飞地
 				if (board[i][j] == 'A') board[i][j] = 'O';	// 还原'O'
			}
		}
	}
};

void my_print(vector<vector<char>> board, string message) {
	cout << message << endl;
	for (vector<vector<char>>::iterator it = board.begin(); it != board.end(); it++) {
		for (vector<char>::iterator jt = (*it).begin(); jt != (*it).end(); jt++) {
			cout << *jt << " ";
		}
		cout << endl;
	}
}

int main() {
	// // 1020、飞地的数量-深度优先搜索-测试案例
    //vector<vector<int>> grid = { {0, 0, 0, 0}, { 1, 0, 1, 0 }, { 0, 1, 1, 0 }, { 0, 0, 0, 0 } };
    //Solution s1;
    //int result = s1.numEnclaves(grid);
    //cout << result << endl;

	// 130、被围绕的区域-深度优先搜索-测试案例
	vector<vector<char>> board = { {'X', 'X', 'X', 'X'}, {'X', 'O', 'O', 'X'}, {'X', 'X', 'O', 'X'}, {'X', 'O', 'X', 'X'} };
	my_print(board, "替换前:");
	Solution2 s1;
	s1.solve(board);
	my_print(board, "替换后:");
    system("pause");
    return 0;
}

end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1462403.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

行测:国考省考行测:图形推理,四面体,正六面体的图形推理方法,箭头唯一法

国考省考行测&#xff1a;图形推理 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能&#xff0c;附带行测和申论&#xff0c;而常规国考省考最重要的还是申论和行测&#xff0c;所以大家认真准备吧&#xff0c;我讲一起屡屡申论和行测的重要知识点 遇到…

(十二)【Jmeter】线程(Threads(Users))之tearDown 线程组

简述 操作路径如下: 作用:在正式测试结束后执行清理操作,如关闭连接、释放资源等。配置:设置清理操作的采样器、执行顺序等参数。使用场景:确保在测试结束后应用程序恢复到正常状态,避免资源泄漏或对其他测试的影响。优点:提供清理操作,确保测试环境的整洁和可重复性…

水经注下载注记地图, mars3d加载底图

使用 水经微图 &#xff08;公司提供的&#xff0c;需付费&#xff0c;我也没有这个东西&#xff09;下载注记地图&#xff1b; 1、选择下载 选择区域&#xff1a; 根据需求进行选择&#xff0c;两边都可以选择&#xff0c;看个人喜欢&#xff1b;这里以澳门为演示 选择地图…

从零开始手写mmo游戏从框架到爆炸(二十一)— 战斗系统二

导航&#xff1a;从零开始手写mmo游戏从框架到爆炸&#xff08;零&#xff09;—— 导航-CSDN博客 上一章&#xff08;从零开始手写mmo游戏从框架到爆炸&#xff08;二十&#xff09;— 战斗系统一-CSDN博客&#xff09;我们只是完成了基本的战斗&#xff0c;速度属性并没有…

学习数仓工具 dbt

DBT 是一个有趣的工具&#xff0c;它通过一种结构化的方式定义了数仓中各种表、视图的构建和填充方式。 dbt 面相的对象是数据开发团队&#xff0c;提供了如下几个最有价值的能力&#xff1a; 支持多种数据库通过 select 来定义数据&#xff0c;无需编写 DML构建数据时&#…

Facebook的未来蓝图:数字社交的下一个篇章

在数字化时代&#xff0c;社交媒体已经成为人们日常生活中不可或缺的一部分。而在众多的社交媒体平台中&#xff0c;Facebook一直处于领先地位&#xff0c;不断探索着数字社交的新领域和新形式。随着科技的不断发展和社会的不断变革&#xff0c;Facebook正在谱写着数字社交的未…

Vue路由缓存问题

路由缓存问题的产生 VueRouter允许用户在页面中创建多个视图&#xff08;多级路由&#xff09;&#xff0c;并根据路由参数来动态的切换视图。使用带参数的路由时&#xff0c;相同的组件实例将被重复使用。因为两个路由都渲染同一个组件&#xff0c;比起销毁再创建&#xff0c;…

谷歌掀桌子!开源Gemma:可商用,性能超过Llama 2!

2月22日&#xff0c;谷歌在官网宣布&#xff0c;开源大语言模型Gemma。 Gemma与谷歌最新发布的Gemini 使用了同一架构&#xff0c;有20亿、70亿两种参数&#xff0c;每种参数都有预训练和指令调优两个版本。 根据谷歌公布的测试显示&#xff0c;在MMLU、BBH、GSM8K等主流测试…

数据结构·顺序表

1数据结构简介 学习数据结构与算法之前&#xff0c;一般是先学数据结构&#xff0c;方便之后学习算法&#xff0c;那么数据结构拆开介绍&#xff0c;就是数据 和 结构&#xff0c;数据&#xff0c;生活中到处都是&#xff0c;结构&#xff0c;就是数据存储的方式&#xff0c;即…

React 事件处理 ( this问题 参数传递 ref)

React事件的命名采用小驼峰方式&#xff08;cameCase&#xff09;,而不是小写 使用JSX语法时你需要传入一个函数作为事件处理函数&#xff0c;而不是一个字符串 你不能通过返回false 的方式阻止默认行为。你必须显示式的使用preventDefault 1 this 需要谨慎对待JSX回调函数中的…

第13讲实现自定义logout处理

默认logout请求实现是有状态的&#xff0c;返回到login请求页面&#xff1b;我们现在是前后端分离处理&#xff0c;所以需要自定义实现logout 新建JwtLogoutSuccessHandler /*** 自定义Logout处理* author java1234_小锋 &#xff08;公众号&#xff1a;java1234&#xff09;…

camunda源代码编译运行(二):构建并运行camunda源代码工程

接上一篇文章&#xff1a;camunda源代码编译运行&#xff08;一&#xff09;&#xff1a;下载编译camunda源代码 Camunda 7.19源代码一共有178个maven工程和1个angular前端工程&#xff0c;这么多工程中包括了大量的QA测试包、JDK不同版本适配&#xff08;比如&#xff1a;Jav…

Vue3之ref与reactive的基本使用

ref可以创建基本类型、对象类型的响应式数据 reactive只可以创建对象类型的响应式数据 接下来让我为大家介绍一下吧&#xff01; 在Vue3中&#xff0c;我们想让数据变成响应式数据&#xff0c;我们需要借助到ref与reactive 先为大家介绍一下ref如何使用还有什么注意点 我们需…

【快速搞定Webpack5】处理样式资源(三)

本次内容我们将学习使用webpack如何处理css、less、sass、scss等样式资源 介绍 webpack本身是不能识别样式资源的&#xff0c;所以我们需要借助loader包来帮助webpack解析样式资源 我们找loader都应该去官方文档中查找对应的loader&#xff0c;然后学习使用。 官方文档找不到…

windows server设置桌面显示此电脑

我开发的chatgpt网站&#xff1a; https://chat.xutongbao.top

Sora - 探索AI视频模型的无限可能-官方报告解读与思考

一、引言 最近SORA火爆刷屏&#xff0c;我也忍不住找来官方报告分析了一下&#xff0c;本文将深入探讨OpenAI最新发布的Sora模型。Sora模型不仅仅是一个视频生成器&#xff0c;它代表了一种全新的数据驱动物理引擎&#xff0c;能够在虚拟世界中模拟现实世界的复杂现象。本文将重…

内网穿透——NPS突然无法连接

温馨提示 &#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f32d;&#x1f32d;&#x1f32d;&#x1f32d;&#x1f32d;&#x1f32d;&#x1f32d;❤️❤️❤️❤️❤️❤️❤️&#x1f968;&#x1f968;&#x1f9…

JavaSprintBoot中一些运维方面的知识

1.配置文件四级分类 例如以下yml配置文件&#xff0c;权限一共有四级&#xff0c;高等级覆盖低等级并叠加&#xff08;权限向下兼容&#xff09; 2.自定义配置文件 可以自定义配置文件的名称&#xff0c;因为实际开发环境中可能不会就简单的叫做application.yml之类的&#x…

《穿越科技的前沿:计算机专业必看的电影盛宴》

文章目录 每日一句正能量前言电影推荐推荐一&#xff1a;《黑客帝国》推荐二&#xff1a;《社交网络》推荐三&#xff1a;《源代码》推荐四&#xff1a;《谍影重重》系列推荐五&#xff1a;《旋转木马》 技术与主题后记 每日一句正能量 一个人的一生&#xff0c;就是一座有了年…

【Linux】MySQL数据库的使用

【Linux】MySQL数据库的使用 一、访问MySQL数据库二、创建及删除库和表1、创建新的库2、创建新的表3、删除一个数据表4、删除一个数据库 三、管理表中的数据记录1、插入数据记录2、查询数据记录3、修改数据记录4、删除数据记录 四、数据库用户授权1、授予权限2、查看权限3、撤销…