代码随想录算法训练day57---图论系列2《岛屿问题》

代码随想录算法训练

—day57


前言

今天是算法营的第57天,希望自己能够坚持下来!
今天继续图论part!今日任务:
● 99.岛屿数量 深搜
● 99.岛屿数量 广搜
● 100.岛屿的最大面积


一、99.岛屿数量 深搜

卡码网题目链接
leetcode题目链接
文章讲解

思路:
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.岛屿数量 广搜

卡码网题目链接
leetcode题目链接
文章讲解

需要注意的是,只要坐标加入队列,就要立刻标记。
如果从队列拿出节点,再去标记这个节点走过,就会发生下图所示的结果,会导致很多节点重复加入队列。
在这里插入图片描述
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. 岛屿的最大面积

卡码网题目链接
leetcode题目链接
文章讲解

深度优先搜索:版本一

跟上一题的岛屿数量思路一样,只需要在找到岛屿时,开始标记与其相连的陆地时记录面积即可。

需要注意的是,计数的重置问题。

如果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,一旦加入队列就要标记。

明天继续加油!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值