代码随想录算法训练
—day57
前言
今天是算法营的第57天,希望自己能够坚持下来!
今天继续图论part!今日任务:
● 99.岛屿数量 深搜
● 99.岛屿数量 广搜
● 100.岛屿的最大面积
一、99.岛屿数量 深搜
思路:
1.用二维数组存放岛屿地图,并且另外用一个visited二维数组记录已经遍历过的坐标。
2.当遍历到visited为false的坐标,说明找到了一个新的岛屿,result+1
3.然后用dfs递归函数去标记跟这个坐标相连的其他陆地坐标
4.寻找下一个岛屿
深搜三部曲:
1.函数参数:岛屿地图grid和标记数组visited,当前x坐标,y坐标
2.终止条件:访问过的节点 或者 遇到海水
3.当前节点的处理:
①for遍历4个方向,模拟向4个方向移动,nextx = x + dir[方向][x方向的增量], nexty = y + dir[方向][y方向的增量],寻找相连的陆地坐标
②需要判断next坐标是否有越界
③如果没有越界,且是没有访问过的陆地坐标,标记访问数组对应元素为true。
(终止条件可以放在开头,if (visited[x][y] || grid[x][y] == 0) return; // 终止条件:访问过的节点 或者 遇到海水;也可以放在调用dfs递归之前)
代码如下:
#include<iostream>
#include<vector>
using namespace std;
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) {
//遍历分别走4个方向的情况
for (int i = 0; i < 4; i++) {
int nextx = x + dir[i][0];
int nexty = y + dir[i][1];
//判断下一步是否在grid中越界
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;
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];
}
}
//标记地图的遍历情况,遍历过的为true
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) { //找到新的岛屿
visited[i][j] = true;
result++; //岛屿数量累加
dfs(grid, visited, i, j); //将跟岛屿相连的陆地标记
}
}
}
cout << result << endl;
return 0;
}
二、99.岛屿数量 广搜
需要注意的是,只要坐标加入队列,就要立刻标记。
如果从队列拿出节点,再去标记这个节点走过,就会发生下图所示的结果,会导致很多节点重复加入队列。
dfs函数:
1.定义一个队列,坐标一加入队列就标记visited,从起点开始加入队列 ( 只要 加入队列就代表 走过,就需要标记,而不是从队列拿出来的时候再去标记走过。)
2.while队列不为空,循环取出队列元素,for遍历4个方向,模拟向4个方向移动,nextx = x + dir[方向][x方向的增量], nexty = y + dir[方向][y方向的增量],寻找相连的陆地坐标
2.需要判断next坐标是否有越界
3.如果没有越界,且是没有访问过的陆地坐标,放入队列,并标记访问数组对应元素为true。
代码如下:
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
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<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. 岛屿的最大面积
深度优先搜索:版本一
跟上一题的岛屿数量思路一样,只需要在找到岛屿时,开始标记与其相连的陆地时记录面积即可。
需要注意的是,计数的重置问题。
如果dfs的终止条件是放在调用递归之前,那么dfs处理的就是下一个节点,那么重置计数的时候需要先把当前节点算上,也就是将面积计数重置成1。
代码如下:
#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>> &visisted, 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 (!visisted[nextx][nexty] && grid[nextx][nexty] == 1) {
count++; //累加面积
visisted[nextx][nexty] = true;
dfs(grid, visisted, 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>> visisted(n, vector<bool>(m, false));
int result = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (!visisted[i][j] && grid[i][j] == 1) {
count = 1; //重置计数,dfs是处理下一个节点,所以这里重置成1
visisted[i][j] = true;
dfs(grid, visisted, i, j); //将与其连接的陆地都标记上true
result = max(result, count);
}
}
}
cout << result << endl;
return 0;
}
深度优先搜索:版本二
如果终止条件是放在dfs开头的话,那么dfs处理的就是当前节点,count从0开始计数。
代码如下:
// 版本二
#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) {
if (visited[x][y] || grid[x][y] == 0) return; // 终止条件:访问过的节点 或者 遇到海水
visited[x][y] = true; // 标记访问过
count++;
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; // 越界了,直接跳过
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 = 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; // 因为dfs处理当前节点,所以遇到陆地计数为0,进dfs之后在开始从1计数
dfs(grid, visited, i, j); // 将与其链接的陆地都标记上 true
result = max(result, count);
}
}
}
cout << result << endl;
}
广度优先搜索
因为bfs是对于本次节点的处理,所以count从0开始计数。代码如下:
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
int count;
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};
void bfs(vector<vector<int>> &grid, vector<vector<bool>> &visisted, int x, int y) {
queue<pair<int, int>> que;
que.push({x, y});
visisted[x][y] = true;
count++;
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 (!visisted[nextx][nexty] && grid[nextx][nexty] == 1) {
count++; //累加面积
que.push({nextx, nexty});
visisted[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>> visisted(n, vector<bool>(m, false));
int result = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (!visisted[i][j] && grid[i][j] == 1) {
count = 0; //重置计数,dfs是处理当前节点,所以这里重置成0
visisted[i][j] = true;
bfs(grid, visisted, i, j); //将与其连接的陆地都标记上true
result = max(result, count);
}
}
}
cout << result << endl;
return 0;
}
总结
1.使用int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};来获取下一步的坐标
2.深度优先搜索dfs的终止条件可以放在开头,也可以放在调用递归前,区别是对函数是处理本次节点还是下一个节点。
3.广度优先搜索bfs,一旦加入队列就要标记。
明天继续加油!