代码随想录算法训练营Day57|99.岛屿数量、100.岛屿的最大面积

岛屿数量

99. 岛屿数量 (kamacoder.com)

广度优先搜索

模板参考代码随想录 (programmercarl.com)

基本思想就是有两个矩阵,分别是图的矩阵graph和遍历过的矩阵visited,两者大小相等,但graph有原值,visited最开始全0,表示为踏足过,每次广度优先搜索会将一块岛屿全走一遍,即visited矩阵在graph的一块岛屿上的面积全部置1。

我们需对矩阵graph每个元素都遍历,在遍历的过程中,若有graph中元素数值为1(是一块岛屿或岛屿的一部分)且是我们之前未踏足过的新大陆(visited = 0),则将结果result + 1,且通过BFS(广度优先搜索)将这块岛屿在visited上标记出来,防止重复踏足。代码如下。

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

// dir数组定义了四个方向,分别代表上、右、下、左的偏移量
int dir[4][2] = {0,1,1,0,-1,0,0,-1};

// 定义一个队列,用于存储待访问的节点(pair<int,int>类型表示坐标)
queue<pair<int,int>> Queue;

// bfs函数实现了广度优先搜索算法
// grid参数表示输入的二维数组(图)
// visited参数表示访问标记数组,用于标记已访问过的节点
// x,y参数表示开始搜索的节点的坐标
void bfs(vector<vector<int>>& grid,vector<vector<int>>& visited, int x, int y){
    Queue.push({x,y});      // 将起始节点加入队列
    visited[x][y] = 1;      // 标记起始节点为已访问

    while(!Queue.empty()){  // 当队列不为空时,继续搜索
        pair<int,int> cur = Queue.front(); // 获取队列中的当前节点
        Queue.pop();                     // 将当前节点从队列中移除

        int cur_x = cur.first;           // 获取当前节点的x坐标
        int cur_y = cur.second;          // 获取当前节点的y坐标

        for(int i = 0; i < 4; i++){      // 遍历四个方向
            int nextx = cur_x + dir[i][0]; // 计算下一个节点的x坐标
            int nexty = cur_y + dir[i][1]; // 计算下一个节点的y坐标

            // 判断下一个节点是否越界
            if(nextx < 0 || nexty < 0 || nextx >= grid.size() || nexty >= grid[0].size())
                continue;   // 如果越界,则跳过当前方向

            // 判断下一个节点是否未被访问且为有效节点(值为1)
            if(!visited[nextx][nexty] && grid[nextx][nexty]){
                Queue.push({nextx,nexty});  // 将下一个节点加入队列
                visited[nextx][nexty] = 1;  // 标记下一个节点为已访问
            }
        }
    }
}

int main() {
    int N,M;                         // N和M分别表示二维数组的行数和列数
    int result = 0;                  // result用于存储连通区域的总数

    cin>>N>>M;                       // 输入二维数组的行数和列数

    // 初始化二维数组graph和访问标记数组visited
    vector<vector<int>> graph(N,vector<int>(M,0));
    vector<vector<int>> visited(N,vector<int>(M,0));

    // 输入二维数组graph的值
    for(int i = 0; i < N;i++){
        for(int j = 0; j < M;j++){
            cin>>graph[i][j];
        }
    }

    // 遍历二维数组graph,对每个未访问过的节点执行bfs
    for(int i = 0; i <N; i++){
        for(int j = 0; j < M; j++){
            if(!visited[i][j] && graph[i][j]){ // 如果节点未被访问且值为1
                bfs(graph,visited,i,j);        // 执行bfs
                result++;                      // 连通区域数加1
            }
        }
    }

    cout<<result<<endl;  // 输出连通区域的总数
    return 0;            // 程序结束
}

算法的时间复杂度为O(m*n),遍历了graph的每一个点,空间复杂度为O(m*n),维护一个visited数组。

深度优先搜索

利用递归来对graph中一个值为1的点四个方位的点进行递归查询,若碰到不符合条件(越界或visited数组值为1时,则自动返回),代码在整体和广度优先搜索差不多,除了使用的dfs而不是bfs,没有使用队列但使用了递归。

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

// dir数组定义了四个方向,分别代表上、右、下、左的偏移量
int dir[4][2] = {0,1,1,0,-1,0,0,-1};

// dfs函数实现了深度优先搜索算法
// grid参数表示输入的二维数组(图)
// visited参数表示访问标记数组,用于标记已访问过的节点
// x,y参数表示当前搜索的节点的坐标
void dfs(const vector<vector<int>>& grid,vector<vector<int>>& visited,int x, int y){
    for(int i = 0; i < 4;i++){              // 遍历四个方向
        int nextx = x + dir[i][0];         // 计算下一个节点的x坐标
        int nexty = y + dir[i][1];         // 计算下一个节点的y坐标

        // 判断下一个节点是否越界
        if(nextx<0 || nexty<0 || nextx >= grid.size()||nexty >= grid[0].size())
            continue;                       // 如果越界,则跳过当前方向

        // 判断下一个节点是否未被访问且为有效节点(值为1)
        if(!visited[nextx][nexty] && grid[nextx][nexty]) {
            visited[nextx][nexty] = 1;      // 标记下一个节点为已访问
            dfs(grid, visited, nextx, nexty); // 递归搜索下一个节点
        }
    }
}

int main() {
    int N,M;                         // N和M分别表示二维数组的行数和列数
    int result = 0;                  // result用于存储连通区域的总数

    cin>>N>>M;                       // 输入二维数组的行数和列数

    // 初始化二维数组graph和访问标记数组visited
    vector<vector<int>> graph(N,vector<int>(M,0));
    vector<vector<int>> visited(N,vector<int>(M,0));

    // 输入二维数组graph的值
    for(int i = 0; i < N;i++){
        for(int j = 0; j < M;j++){
            cin>>graph[i][j];
        }
    }

    // 遍历二维数组graph,对每个未访问过的节点执行dfs
    for(int i = 0; i <N; i++){
        for(int j = 0; j < M; j++){
            if(!visited[i][j] && graph[i][j]){ // 如果节点未被访问且值为1
                result++;                      // 连通区域数加1
                dfs(graph,visited,i,j);        // 执行dfs
            }
        }
    }

    cout<<result<<endl;  // 输出连通区域的总数
    return 0; 
}

算法的时间复杂度和空间复杂度与上题相同,这里需要注意的是,递归调用栈的最大深度为N*M(每个节点都递归调用一次),所以空间复杂度整体上没有变化。

岛屿的最大面积

100. 岛屿的最大面积 (kamacoder.com)

在每次广度搜索或者深度搜索中,会对visited数组进行置1,比较每次置1的数目,即可求得岛屿最大面积。

广度优先搜索

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

// dir数组定义了四个方向,分别代表上、右、下、左的偏移量
int dir[4][2] = {0,1,1,0,-1,0,0,-1};

// 定义一个队列,用于存储待访问的节点(pair<int,int>类型表示坐标)
queue<pair<int,int>> Queue;

// bfs函数实现了广度优先搜索算法
// grid参数表示输入的二维数组(图)
// visited参数表示访问标记数组,用于标记已访问过的节点
// x,y参数表示开始搜索节点的下标
void bfs(vector<vector<int>>& grid,vector<vector<int>>& visited, int x, int y){
    Queue.push({x,y});      // 将起始节点加入队列
    visited[x][y] = 1;      // 标记起始节点为已访问

    while(!Queue.empty()){  // 当队列不为空时,继续搜索
        pair<int,int> cur = Queue.front(); // 获取队列中的当前节点
        Queue.pop();                     // 将当前节点从队列中移除

        int cur_x = cur.first;           // 获取当前节点的x坐标
        int cur_y = cur.second;          // 获取当前节点的y坐标

        for(int i = 0; i < 4; i++){      // 遍历四个方向
            int nextx = cur_x + dir[i][0]; // 计算下一个节点的x坐标
            int nexty = cur_y + dir[i][1]; // 计算下一个节点的y坐标

            // 判断下一个节点是否越界
            if(nextx < 0 || nexty < 0 || nextx >= grid.size() || nexty >= grid[0].size())
                continue;   // 如果越界,则跳过当前方向

            // 判断下一个节点是否未被访问且为有效节点(值为1)
            if(!visited[nextx][nexty] && grid[nextx][nexty]){
                Queue.push({nextx,nexty});  // 将下一个节点加入队列
                visited[nextx][nexty] = 1;  // 标记下一个节点为已访问
            }
        }
    }
}

int main() {
    int N,M;                         // N和M分别表示二维数组的行数和列数
    int max_area = 0;                // max_area用于存储最大连通区域的大小
    int sumofvisitedlasted = 0;      // sumofvisitedlasted用于存储上一次遍历的总访问节点数
    int sumofvisited = 0;            // sumofvisited用于存储当前遍历的总访问节点数

    cin>>N>>M;                       // 输入二维数组的行数和列数

    // 初始化二维数组graph和访问标记数组visited
    vector<vector<int>> graph(N,vector<int>(M,0));
    vector<vector<int>> visited(N,vector<int>(M,0));

    // 输入二维数组graph的值
    for(int i = 0; i < N;i++){
        for(int j = 0; j < M;j++){
            cin>>graph[i][j];
        }
    }

    // 遍历二维数组graph,对每个未访问过的节点执行bfs
    for(int i = 0; i <N; i++){
        for(int j = 0; j < M; j++){
            if(!visited[i][j] && graph[i][j]){ // 如果节点未被访问且值为1
                bfs(graph,visited,i,j);        // 执行bfs
                for(int i = 0; i < N;i++){     // 计算当前遍历的总访问节点数
                    for(int j = 0 ; j < M; j++){
                        sumofvisited+=visited[i][j];
                    }
                }
                max_area = max(max_area,sumofvisited - sumofvisitedlasted); // 更新最大连通区域的大小
                sumofvisitedlasted = sumofvisited;                         // 更新上一次遍历的总访问节点数
                sumofvisited = 0;                                          // 重置当前遍历的总访问节点数
            }
        }
    }

    cout<<max_area<<endl;  // 输出最大连通

算法的时间复杂度为O(M*N),空间复杂度为O(M*N)。

深度优先搜索

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

// dir数组定义了四个方向,分别代表上、右、下、左的偏移量
int dir[4][2] = {0,1,1,0,-1,0,0,-1};

// dfs函数实现了深度优先搜索算法
// grid参数表示输入的二维数组(图)
// visited参数表示访问标记数组,用于标记已访问过的节点
// x,y参数表示当前搜索节点的下标
void dfs(const vector<vector<int>>& grid, vector<vector<int>>& visited,int x, int y){
    for(int i = 0; i < 4; i ++){              // 遍历四个方向
        int nextx = x + dir[i][0];           // 计算下一个节点的x坐标
        int nexty = y + dir[i][1];           // 计算下一个节点的y坐标

        // 判断下一个节点是否越界
        if(nextx<0 or nexty < 0 or nextx>=grid.size() or nexty >= grid[0].size())
            continue;                       // 如果越界,则跳过当前方向

        // 判断下一个节点是否未被访问且为有效节点(值为1)
        if(!visited[nextx][nexty] and grid[nextx][nexty]){
            visited[nextx][nexty] = 1;      // 标记下一个节点为已访问
            dfs(grid,visited,nextx,nexty); // 递归搜索下一个节点
        }
    }
}

int main() {
    int N,M;                         // N和M分别表示二维数组的行数和列数
    int max_area = 0;                // max_area用于存储最大连通区域的大小
    int sumofvisitedlasted = 0;      // sumofvisitedlasted用于存储上一次遍历的总访问节点数
    int sumofvisited = 0;            // sumofvisited用于存储当前遍历的总访问节点数

    cin>>N>>M;                       // 输入二维数组的行数和列数

    // 初始化二维数组graph和访问标记数组visited
    vector<vector<int>> graph(N,vector<int>(M,0));
    vector<vector<int>> visited(N,vector<int>(M,0));

    // 输入二维数组graph的值
    for(int i = 0; i < N;i++){
        for(int j = 0; j < M;j++){
            cin>>graph[i][j];
        }
    }

    // 遍历二维数组graph,对每个未访问过的节点执行dfs
    for(int i = 0; i <N; i++){
        for(int j = 0; j < M; j++){
            if(!visited[i][j] and graph[i][j]){ // 如果节点未被访问且值为1
                visited[i][j] = 1;              // 标记起始节点为已访问
                dfs(graph,visited,i,j);         // 执行dfs
                for(int i = 0; i < N;i++){       // 计算当前遍历的总访问节点数
                    for(int j = 0 ; j < M; j++){
                        sumofvisited+=visited[i][j];
                    }
                }
                max_area = max(max_area,sumofvisited - sumofvisitedlasted); // 更新最大连通区域的大小
                sumofvisitedlasted = sumofvisited;                         // 更新上一次遍历的总访问节点数
                sumofvisited = 0;                                          // 重置当前遍历的总访问节点数
            }
        }
    }

    cout<<max_area<<endl;  // 输出最大连通区域的大小
    return 0;
}

算法的时间复杂度和空间复杂度为O(N*M)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值