第51天,图论02,岛屿题型💪(ง •_•)ง,编程语言C++
目录
99.岛屿数量 深搜
文档讲解:手撕岛屿数量 深搜
学习:本题首先要理解题意,明确岛屿划分的方法。岛屿是只能由水平方向和竖直方向上相邻的陆地连接形成的,也就是说斜角度连接是不算的,因此一个陆地只有四个向外扩展的方向。
本题的思路是遇到一个没有标记过的节点陆地,计数器就加一(岛屿数量加一),然后把这个节点周围相连的陆地都标记上(属于同一个岛屿)。
之后继续遍历图,如果遇到标记过的陆地,或者海洋就跳过,直到下一个没有标记过的陆地,也就是一个新的岛屿。
代码:十分要注意仔细书写,不要写错代码!!!
#include <iostream>
#include <vector>
using namespace std;
int dir[4][2] = {{0,1}, {1,0}, {-1, 0}, {0, -1}}; //四个方向(根据x,y来调整方向)
//1.确定返回值,及参数列表
void dfs(const vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y) { //dfs
for(int i = 0; i < 4; i++) {
int nextx = x + dir[i][0]; //确定下一个寻找的方向
int nexty = y + dir[i][1];
if(nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; //越界
//2.终止条件:只有复合条件才进入递归
if(!visited[nextx][nexty] && grid[nextx][nexty] == 1) { //如果当前位置没有被标记且是陆地,这里注意该陆地是和我们找到的岛屿一起的
//3.处理当前节点,单层递归逻辑
visited[nextx][nexty] = true; //进行标记
dfs(grid, visited, nextx, nexty); //查找更多的陆地
}
}
}
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> grid(n, vector<int>(m, 0));//保存地图
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
cin >> grid[i][j];
}
}
vector<vector<bool>> visited(n, vector<bool>(m, false)); //标记地图上的点
int result = 0; //记录岛屿数量
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(!visited[i][j] && grid[i][j] == 1) { //如果当前点没有被标记,且是陆地的情况下,我们认为找到了一个岛屿
result++; //我们认为找到一个岛屿
visited[i][j] == true; //进行标记
dfs(grid, visited, i, j); //标记这片岛屿的所有陆地
}
}
}
cout << result << endl;
return 0;
}
本题终止条件是写在了调用dfs的地方,如果遇到不合法的方向,直接不会去调用dfs。当然写在开头也是可以的,只不过会多一些不必要的递归。
99.岛屿数量 广搜
文档讲解:手撕岛屿数量 广搜
学习:上面我们采用了深搜的方式进行本题的求解。本题我们还可以采用广搜(广度优先搜索bfs)进行求解。
本题采用广度优先搜索方法,主要需要注意防止超时。对于广搜来说我们要注意:加入队列就代表走过了,就需要进行标记,而不是队列拿出来的时候,再进行标记。区别就在于,如果从队列拿出来的时候,再进行标记,会导致出现重复加入队列的情况。(如下图所示)
因此本题的代码为:
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int dir[4][2] = {{0,1}, {1,0}, {-1, 0}, {0, -1}}; //四个方向(根据x,y来调整方向)
//采用广度优先搜索的办法
void bfs(const vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y) { //bfs
queue<pair<int, int>> que;
que.push({x, y});
visited[x][y] = true;
while(!que.empty()) {
pair<int, int> cur = que.front(); //取出遍历节点
que.pop();
int curx = cur.first;
int cury = cur.second;
for(int i = 0; i < 4; i++) {
int nextx = curx + dir[i][0];
int nexty = cury + dir[i][1];
if(nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; //越界
if(!visited[nextx][nexty] && grid[nextx][nexty] == 1) { //与我们找到的岛屿连接在一起的陆地
que.push({nextx, nexty});
visited[nextx][nexty] = true;
}
}
}
}
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> grid(n, vector<int>(m, 0));//保存地图
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
cin >> grid[i][j];
}
}
vector<vector<bool>> visited(n, vector<bool>(m, false)); //标记地图上的点
int result = 0; //记录岛屿数量
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(!visited[i][j] && grid[i][j] == 1) { //如果当前点没有被标记,且是陆地的情况下,我们认为找到了一个岛屿
result++; //我们认为找到一个岛屿
bfs(grid, visited, i, j); //标记这片岛屿的所有陆地
}
}
}
cout << result << endl;
return 0;
}
100.岛屿的最大面积
文档讲解:手撕岛屿的最大面积
题目:100. 岛屿的最大面积 (kamacoder.com)
学习:本题实际上和上一题是一样的,只不过上一题要求求的是岛屿的数量,本题要求求的是岛屿的最大面积。采用dfs和bfs方法都可以,只需要增加一个count量,来记录每次找到的岛屿的面积即可。
代码:dfs
#include <iostream>
#include <vector>
using namespace std;
int count;
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四个方向
void dfs(vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y) {
for (int i = 0; i < 4; i++) {
int nextx = x + dir[i][0];
int nexty = y + dir[i][1];
if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; // 越界了,直接跳过
if (!visited[nextx][nexty] && grid[nextx][nexty] == 1) { // 没有访问过的 同时 是陆地的
visited[nextx][nexty] = true;
count++; //因为是全局变量,会一直记录到下一个岛屿重置为1为止
dfs(grid, visited, nextx, nexty);
}
}
}
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> grid(n, vector<int>(m, 0));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> grid[i][j];
}
}
vector<vector<bool>> visited(n, vector<bool>(m, false));
int result = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (!visited[i][j] && grid[i][j] == 1) {
count = 1; // 找到一个新岛屿,重置计数值,重新从1开始计算
visited[i][j] = true;
dfs(grid, visited, i, j); // 将与其链接的陆地都标记上 true
result = max(result, count);
}
}
}
cout << result << endl;
}
代码:bfs
class Solution {
private:
int count;
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四个方向
void bfs(vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y) {
queue<int> que;
que.push(x);
que.push(y);
visited[x][y] = true; // 加入队列就意味节点是陆地可到达的点
count++;
while(!que.empty()) {
int xx = que.front();que.pop();
int yy = que.front();que.pop();
for (int i = 0 ;i < 4; i++) {
int nextx = xx + dir[i][0];
int nexty = yy + dir[i][1];
if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; // 越界
if (!visited[nextx][nexty] && grid[nextx][nexty] == 1) { // 节点没有被访问过且是陆地
visited[nextx][nexty] = true;
count++;
que.push(nextx);
que.push(nexty);
}
}
}
}
public:
int maxAreaOfIsland(vector<vector<int>>& grid) {
int n = grid.size(), m = grid[0].size();
vector<vector<bool>> visited = vector<vector<bool>>(n, vector<bool>(m, false));
int result = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (!visited[i][j] && grid[i][j] == 1) {
count = 0;
bfs(grid, visited, i, j); // 将与其链接的陆地都标记上 true
result = max(result, count);
}
}
}
return result;
}
};
总结
该两题是dfs和bfs最基础的题目,直接考察dfs和bfs的书写能力。相较于其他算法dfs和bfs的代码很多,要十分注意书写错误(书写错误带来的调debug的难度很高)。多加练习!!!加强书写!!!