岛屿问题---DFS和BFS

问题1、岛屿数量

题目描述

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

示例 1:

输入:grid = [
  ["1","1","1","1","0"],
  ["1","1","0","1","0"],
  ["1","1","0","0","0"],
  ["0","0","0","0","0"]
]
输出:1

思路1---DFS

我们可以将二维网格看成一个无向图,竖直或水平相邻的 1 之间有边相连。

        为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 1,则以其为起始节点开始进行深度优先搜索。在深度优先搜索的过程中,每个搜索到的 1 都会被重新标记为 0。

最终岛屿的数量就是我们进行深度优先搜索的次数

复杂度分析

时间复杂度:O(MN),其中 M 和 N 分别为行数和列数。

空间复杂度:O(MN),在最坏情况下,整个网格均为陆地,深度优先搜索的深度达到 MN。

思路2---BFS 

同样地,我们也可以使用广度优先搜索代替深度优先搜索。

        为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为 1,则将其加入队列,开始进行广度优先搜索。在广度优先搜索的过程中,每个搜索到的 1 都会被重新标记为 0。直到队列为空,搜索结束。

最终岛屿的数量就是我们进行广度优先搜索的次数

复杂度分析

时间复杂度:O(MN),其中 M 和 N 分别为行数和列数。

空间复杂度:O(min(M,N)),在最坏情况下,整个网格均为陆地,队列的大小可以达到 min(M,N)。

整合代码

#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <sstream>

using namespace std;

class Solution_Number_DFS {//岛屿数量
private:
    // 上下左右四个方向的数组
    vector<pair<int, int>> DIRECTIONS = { {0, 1}, {1, 0}, {0, -1}, {-1, 0} };

    // 构建DFS函数
    void DFS(vector<vector<string>>& grid, int i, int j, vector<vector<bool>>& checkList) {
        // 将该点标记为已经检查过
        checkList[i][j] = true;

        // 遍历上下左右四个方向的邻点坐标
        for (auto& dir : DIRECTIONS) {
            int next_i = i + dir.first;
            int next_j = j + dir.second;

            // 若近邻点满足三个条件:
            // 1.没有越界    2. 近邻点尚未被检查过   3.近邻点也为陆地
            if (next_i >= 0 && next_i < grid.size() && next_j >= 0 && next_j < grid[0].size()
                && !checkList[next_i][next_j] && grid[next_i][next_j] == "1") {
                // 对近邻点进行DFS搜索
                DFS(grid, next_i, next_j, checkList);
            }
        }
    }

public:
    int numIslands(vector<vector<string>>& grid) {
        int x = grid.size();    // 获得网格长
        int y = grid[0].size(); // 获得网格宽
        int ans = 0;            // 初始化答案为0

        // 初始化数组checkList用于DFS遍历过程中的检查
        // false表示尚未访问,true表示已经访问
        vector<vector<bool>> checkList(x, vector<bool>(y, false));

        // 对整个grid二维数组进行双重循环遍历
        for (int i = 0; i < x; i++) {
            for (int j = 0; j < y; j++) {
                // 若该点为陆地且还没有进行过搜寻
                if (grid[i][j] == "1" && !checkList[i][j]) {
                    // 可以进行DFS
                    DFS(grid, i, j, checkList);
                    // 做完DFS,连通块数量+1,更新答案
                    ans++;
                }
            }
        }
        return ans;
    }
};

class Solution_Number_BFS {
private:
    // 上下左右四个方向的数组
    vector<pair<int, int>> DIRECTIONS = { {0,1}, {1,0}, {0,-1}, {-1,0} };

public:
    int numIslands(vector<vector<string>>& grid) {
        int xLen = grid.size();    // 获得网格长
        int yLen = grid[0].size(); // 获得网格宽
        int ans = 0;               // 初始化陆地数目为0

        // 初始化和grid一样大小的二维数组checkList用于BFS遍历过程中的检查
        vector<vector<int>> checkList(xLen, vector<int>(yLen, 0));

        // 双重遍历grid数组
        for (int i = 0; i < xLen; i++) {
            for (int j = 0; j < yLen; j++) {
                // 若该点为陆地且还没有进行过搜寻
                // 找到了一个BFS搜索的起始位置(i,j)
                if (grid[i][j] == "1" && checkList[i][j] == 0) {
                    // 对于该片连通块,构建一个队列,初始化包含该点
                    queue<pair<int, int>> q;
                    q.push({ i, j });
                    // 修改checkList[i][j]为1,表示该点已经搜寻过
                    checkList[i][j] = 1;

                    // 进行BFS,退出循环的条件是队列为空
                    while (!q.empty()) {
                        // 弹出队头的点(x,y),搜寻该点上下左右的近邻点
                        int x = q.front().first;
                        int y = q.front().second;
                        q.pop();

                        // 遍历(x,y)上下左右的四个方向的近邻点
                        for (auto& dir : DIRECTIONS) {
                            int xNext = x + dir.first;
                            int yNext = y + dir.second;

                            // 如果近邻点满足三个条件
                            if (xNext >= 0 && xNext < xLen && yNext >= 0 && yNext < yLen
                                && checkList[xNext][yNext] == 0 && grid[xNext][yNext] == "1") {
                                // 对近邻点做两件事:
                                // 1. 入队       2. 标记为已检查过
                                q.push({ xNext, yNext });
                                checkList[xNext][yNext] = 1;
                            }
                        }
                    }
                    // 连通块的数量+1
                    ans++;
                }
            }
        }
        return ans;
    }
};

vector<int> split(string params_str) {
    vector<int> p;
    while (params_str.find(",") != string::npos) {
        int found = params_str.find(",");
        p.push_back(stoi(params_str.substr(0, found)));
        params_str = params_str.substr(found + 1);
    }
    p.push_back(stoi(params_str));
    return p;
}



vector<string> split_str(string params_str) {
    vector<string> p;
    while (params_str.find(",") != string::npos) {
        int found = params_str.find(",");
        p.push_back((params_str.substr(0, found)));
        params_str = params_str.substr(found + 1);
    }
    p.push_back((params_str));
    return p;
}


vector<vector<string>> v_2dim;
vector<string> p;
vector<vector<string>> split_2_dimention(string s) {
    s += "  ";//防止最后一个]后面没有元素所导致的越界
    while (s.find("]") != string::npos) {
        int found_right = s.find("]");
        string s1 = s.substr(1, found_right - 1);
        p = split_str(s1);
        v_2dim.push_back(p);
        s = s.substr(found_right + 2);//取第二个vector,这里可能会越界
        p.clear();
    }
    return v_2dim;
}


//[[1,1,0,0,0],[1,1,0,0,0],[0,0,1,0,0],[0,0,0,1,1]]
int main() {
    string input;
    getline(cin, input);
    vector<vector<string>> grids;

    grids = split_2_dimention(input);
    Solution_Number_BFS obj;
    int ans = obj.numIslands(grids);
    cout << ans;
}

 

问题2、最大岛屿面积

题目描述

给你一个大小为 m x n 的二进制矩阵 grid 。

岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

岛屿的面积是岛上值为 1 的单元格的数目。

计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 。

输入:grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,1,1,0,1,0,0,0,0,0,0,0,0],[0,1,0,0,1,1,0,0,1,0,1,0,0],[0,1,0,0,1,1,0,0,1,1,1,0,0],[0,0,0,0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,0,0,0,0,0,0,1,1,0,0,0,0]]
输出:6
解释:答案不应该是 11,因为岛屿只能包含水平或垂直这四个方向上的 1。

思路1---DFS

我们想知道网格中每个连通形状的面积,然后取最大值。

        如果我们在一个土地上,以 4 个方向探索与之相连的每一个土地(以及与这些土地相连的土地),那么探索过的土地总数将是该连通形状的面积。

        为了确保每个土地访问不超过一次,我们每次经过一块土地时,将这块土地的值置为 0。这样我们就不会多次访问同一土地。

代码 

#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <sstream>

using namespace std;

class Solution_Area_DFS {//最大岛屿面积
private:
    vector<vector<int>> DIRECTIONS = { {0, 1}, {1, 0}, {0, -1}, {-1, 0} };
    int x = 0;
    int y = 0;
    int area = 0;

    void DFS(vector<vector<int>>& grid, int i, int j, vector<vector<bool>>& checkList) {
        // 将该点标记为已经检查过
        checkList[i][j] = true;
        // 面积增大
        area++;
        // 遍历上下左右四个方向的邻点坐标
        for (auto& dir : DIRECTIONS) {
            int next_i = i + dir[0];
            int next_j = j + dir[1];
            // 若近邻点满足三个条件:
            // 1.没有越界    2. 近邻点尚未被检查过   3.近邻点也为陆地
            if (next_i >= 0 && next_i < x && next_j >= 0 && next_j < y &&
                !checkList[next_i][next_j] && grid[next_i][next_j] == 1) {
                // 对近邻点(next_i, next_j)进行DFS搜索
                DFS(grid, next_i, next_j, checkList);
            }
        }
    }

public:
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        x = grid.size();         // 获得网格长
        y = grid[0].size();      // 获得网格宽
        int ans = 0;             // 初始化答案为0
        // 初始化数组checkList用于DFS遍历过程中的检查
        // false表示尚未访问,true表示已经访问
        vector<vector<bool>> checkList(x, vector<bool>(y, false));

        // 对整个grid二维数组进行双重循环遍历
        for (int i = 0; i < x; i++) {
            for (int j = 0; j < y; j++) {
                // 若该点为陆地且还没有进行过搜寻
                if (grid[i][j] == 1 && !checkList[i][j]) {
                    // 在每一次DFS之前,先初始化面积为0
                    area = 0;//如果不初始化为0,则答案是输出所有岛屿面积之和
                    // 可以进行DFS
                    DFS(grid, i, j, checkList);
                    // 做完DFS,更新ans
                    ans = max(ans, area);
                }
            }
        }
        return ans;
    }
};


vector<int> split(string params_str) {
    vector<int> p;
    while (params_str.find(",") != string::npos) {
        int found = params_str.find(",");
        p.push_back(stoi(params_str.substr(0, found)));
        params_str = params_str.substr(found + 1);
    }
    p.push_back(stoi(params_str));
    return p;
}

vector<vector<int>> v_2dim;
vector<int> p;
vector<vector<int>> split_2_dimention(string s) {
    s += "  ";//防止最后一个]后面没有元素所导致的越界
    while (s.find("]") != string::npos) {
        int found_right = s.find("]");
        string s1 = s.substr(1, found_right - 1);
        p = split(s1);
        v_2dim.push_back(p);
        s = s.substr(found_right + 2);//取第二个vector,这里可能会越界
        p.clear();
    }
    return v_2dim;
}


/*
[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]]
*/
int main() {
    
    string input_str;
    cin >> input_str;
    vector<vector<int>> grid;
    grid = split_2_dimention(input_str.substr(1, input_str.size() - 2));
    Solution_Area_DFS a;
    cout << a.maxAreaOfIsland(grid) << endl;//6
}

复杂度分析

时间复杂度:O(m×n)。其中 m 是给定网格中的行数,n 是列数。我们访问每个网格最多一次。

空间复杂度:O(m×n),递归的深度最大可能是整个网格的大小,因此最大可能使用 O(m×n) 的栈空间。

思路2---BFS

代码

#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <sstream>

using namespace std;

class Solution_Area_BFS {
private:
    vector<vector<int>> DIRECTIONS = { {0, 1}, {1, 0}, {0, -1}, {-1, 0} };

public:
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        int xLen = grid.size();         // 获得网格长
        int yLen = grid[0].size();      // 获得网格宽
        int ans = 0;                    // 初始化陆地数目为0
        // 初始化和 grid 一样大小的二维数组 checkList 用于 BFS 遍历过程中的检查
        vector<vector<int>> checkList(xLen, vector<int>(yLen, 0));
        // 双重遍历 grid 数组
        for (int i = 0; i < xLen; i++) {
            for (int j = 0; j < yLen; j++) {
                // 若该点为陆地且还没有进行过搜寻
                // 找到了一个 BFS 搜索的起始位置 (i, j)
                if (grid[i][j] == 1 && checkList[i][j] == 0) {
                    // 对于该片连通块,构建一个队列,初始化包含该点
                    queue<pair<int, int>> q;
                    q.push(make_pair(i, j));
                    // 修改 checkList[i][j] 为 1,表示该点已经搜寻过
                    checkList[i][j] = 1;
                    // 进行 BFS 之前,初始化该连通块的面积为 0
                    int area = 0;
                    // 进行 BFS,退出循环的条件是队列为空
                    while (!q.empty()) {
                        // 弹出栈队头的点 (x, y),搜索该点上下左右的近邻点
                        pair<int, int> point = q.front();
                        q.pop();
                        int x = point.first;
                        int y = point.second;
                        area++;//面积加1
                        // 遍历 (x, y) 上下左右的四个方向的近邻点
                        for (auto& dir : DIRECTIONS) {
                            int xNext = x + dir[0];
                            int yNext = y + dir[1];
                            // 如果近邻点满足三个条件
                            if (xNext >= 0 && xNext < xLen && yNext >= 0 && yNext < yLen
                                && checkList[xNext][yNext] == 0 && grid[xNext][yNext] == 1) {
                                // 对近邻点做两件事:
                                // 1. 入队       2. 标记为已检查过
                                q.push(make_pair(xNext, yNext));
                                checkList[xNext][yNext] = 1;
                            }
                        }
                    }
                    // 更新答案
                    ans = max(ans, area);
                }
            }
        }
        return ans;
    }
};


vector<int> split(string params_str) {
    vector<int> p;
    while (params_str.find(",") != string::npos) {
        int found = params_str.find(",");
        p.push_back(stoi(params_str.substr(0, found)));
        params_str = params_str.substr(found + 1);
    }
    p.push_back(stoi(params_str));
    return p;
}

vector<vector<int>> v_2dim;
vector<int> p;
vector<vector<int>> split_2_dimention(string s) {
    s += "  ";//防止最后一个]后面没有元素所导致的越界
    while (s.find("]") != string::npos) {
        int found_right = s.find("]");
        string s1 = s.substr(1, found_right - 1);
        p = split(s1);
        v_2dim.push_back(p);
        s = s.substr(found_right + 2);//取第二个vector,这里可能会越界
        p.clear();
    }
    return v_2dim;
}


/*
[[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]]
*/
int main() {
    
    string input_str;
    cin >> input_str;
    vector<vector<int>> grid;
    grid = split_2_dimention(input_str.substr(1, input_str.size() - 2));
    Solution_Area_BFS a;
    cout << a.maxAreaOfIsland(grid) << endl;//6
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值