力扣第200题 岛屿数量 C++ dfs bfs 深搜和广搜 附Java代码

题目

200. 岛屿数量

中等

相关标签

深度优先搜索 广度优先搜索 并查集 数组 矩阵

给你一个由 '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

示例 2:

复制代码
输入:grid = [
  ["1","1","0","0","0"],
  ["1","1","0","0","0"],
  ["0","0","1","0","0"],
  ["0","0","0","1","1"]
]
输出:3

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 300
  • grid[i][j] 的值为 '0''1'

思路和解题方法 dfs (深度优先搜索)

  1. 当使用深度优先搜索(DFS)解决岛屿数量问题时,我们从网格的每个位置开始,尝试向四个方向扩展,以找到相邻的陆地。具体来说,对于当前位置 (x, y),我们首先检查它是否是未访问过的陆地,并且将其标记为已访问。然后,我们继续向当前位置的四个相邻位置进行扩展搜索,如果这些相邻位置也是未访问过的陆地,则将其标记为已访问,并对这些位置再进行 DFS。通过不断地递归调用 DFS,我们可以逐步标记出与当前位置连通的所有陆地。最终,一个完整的 DFS 过程能够探索完整个岛屿的范围。
  2. 在代码中,DFS 函数被设计用来实现上述的搜索过程。通过对每个陆地位置调用 DFS 函数,我们可以找到并标记出一个完整的岛屿。而在主函数 numIslands 中,我们遍历整个网格,对于每个未访问过的陆地(grid[i][j] == '1' && !visited[i][j]),我们将其标记为已访问,并将岛屿数量加一,然后通过调用 DFS 函数来探索并标记出当前岛屿的全部位置。
  3. 这种深度优先搜索的思维方式,类似于一种 "探险" 的过程,从一个位置出发,不断地向着不同的方向探索,直到无法继续为止,然后返回到之前的位置继续探索。通过这种方式,我们可以逐步发现并标记出完整的岛屿,同时统计出岛屿的数量。

复杂度

时间复杂度:

O(m*n)

时间复杂度:

  • 在这段代码中,我们需要遍历整个二维网格,假设网格的大小为 mn,那么时间复杂度为 O(mn)。
  • 在 DFS 过程中,最坏情况下,我们可能需要访问所有的陆地位置,因此 DFS 的时间复杂度也为 O(m*n)。

因此,综合考虑,这段代码的时间复杂度为 O(m*n)。

空间复杂度

O(m*n)

空间复杂度:

  • 代码中使用了一个二维数组 visited 用来记录每个位置是否已被访问过,其大小与输入的二维网格大小相同,因此空间复杂度为 O(m*n)。
  • 在 DFS 函数的递归调用过程中,栈的深度最大可能达到网格的大小,因此最坏情况下,递归调用的空间复杂度也为 O(m*n)。

因此,综合考虑,这段代码的空间复杂度为 O(m*n)。

c++ 代码 一

cpp 复制代码
class Solution {
public:
    int fanxiang[4][2] = {0,1,1,0,-1,0,0,-1};  // 定义方向数组,表示上、下、左、右四个方向的坐标偏移量

    // 定义深度优先搜索函数,用于遍历grid中的岛屿
    void dfs(vector<vector<char>> &grid, vector<vector<bool>> &visited, int x, int y) {
        for (int i = 0; i < 4; i++) {
            int nextx = x + fanxiang[i][0];  // 计算下一个节点的横坐标
            int nexty = y + fanxiang[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;
                dfs(grid, visited, nextx, nexty);
            }
        }
    }

    // 主函数,用于计算岛屿的数量
public:
    int numIslands(vector<vector<char>>& grid) {
        int n = grid.size();  // 获取网格的行数
        int m = grid[0].size();  // 获取网格的列数
        vector<vector<bool>> visited = vector<vector<bool>>(n, vector<bool>(m, false));  // 初始化访问标记数组
        int ans = 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;  // 标记当前节点为已访问
                    ans++;  // 岛屿数量加一
                    dfs(grid, visited, i, j);  // 开始深度优先搜索
                }
            }
        }
        return ans;  // 返回岛屿数量
    }
};

思路和解题方法 bfs (广度优先搜索)

  1. 首先,我们遍历给定的二维网格,对于每一个位置 (r, c),如果该位置的值为 '1',表示是陆地,则我们开始一个新的岛屿搜索。

  2. 我们将当前位置 (r, c) 标记为已访问,然后从这个位置开始向周围进行扩散搜索。我们使用一个队列 neighbors 来存储相邻的陆地位置。

  3. 在每一次的扩散搜索中,我们取出队列的首部元素 (row, col),然后探索其上、下、左、右四个方向上的相邻位置。对于每一个相邻位置,如果是陆地且尚未访问过,则将其加入队列,并标记为已访问。

  4. 我们不断重复这个过程,直到队列为空,表示当前岛屿的所有陆地位置都被访问过。此时一个岛屿的搜索结束,我们将岛屿数量加 1。

  5. 最后,我们继续遍历整个网格,重复上述过程,直到所有的位置都被访问过。

复杂度

时间复杂度:

O(m*n)

时间复杂度取决于网格中的元素数量。假设网格的行数为 m,列数为 n,那么时间复杂度为 O(m*n)。因为我们需要遍历整个网格,并且对每个元素进行一次搜索。

空间复杂度

O(m*n)

空间复杂度,最坏情况下,队列 neighbors 可能存储整个岛屿的所有陆地位置,因此空间复杂度可能达到 O(m*n)。

c++ 代码二

cpp 复制代码
class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        // 获取网格的行数和列数
        int nr = grid.size();
        if (!nr) return 0; // 如果行数为 0,返回岛屿数量为 0
        int nc = grid[0].size();

        int num_islands = 0; // 初始化岛屿数量为 0
        for (int r = 0; r < nr; ++r) { // 遍历行
            for (int c = 0; c < nc; ++c) { // 遍历列
                if (grid[r][c] == '1') { // 如果当前位置是陆地
                    ++num_islands; // 岛屿数量加一
                    grid[r][c] = '0'; // 将当前位置标记为已访问
                    queue<pair<int, int>> neighbors; // 创建一个队列用于存储相邻的陆地位置
                    neighbors.push({r, c}); // 将当前位置加入队列
                    while (!neighbors.empty()) { // 当队列不为空时进行循环
                        auto rc = neighbors.front(); // 取出队首元素
                        neighbors.pop(); // 弹出队首元素
                        int row = rc.first, col = rc.second; // 获取行和列坐标
                        // 探索四个方向上的相邻位置,将相邻的陆地位置加入队列,并标记为已访问
                        if (row - 1 >= 0 && grid[row-1][col] == '1') {
                            neighbors.push({row-1, col});
                            grid[row-1][col] = '0';
                        }
                        if (row + 1 < nr && grid[row+1][col] == '1') {
                            neighbors.push({row+1, col});
                            grid[row+1][col] = '0';
                        }
                        if (col - 1 >= 0 && grid[row][col-1] == '1') {
                            neighbors.push({row, col-1});
                            grid[row][col-1] = '0';
                        }
                        if (col + 1 < nc && grid[row][col+1] == '1') {
                            neighbors.push({row, col+1});
                            grid[row][col+1] = '0';
                        }
                    }
                }
            }
        }

        return num_islands; // 返回岛屿数量
    }
};

Java代码 一

java 复制代码
class Solution {
    // 深度优先搜索函数,用于将当前陆地位置及其相邻的陆地全部标记为已访问
    void dfs(char[][] grid, int r, int c) {
        int nr = grid.length;  // 获取网格的行数
        int nc = grid[0].length;  // 获取网格的列数

        // 递归结束条件:当前位置超出边界或者当前位置是水域
        if (r < 0 || c < 0 || r >= nr || c >= nc || grid[r][c] == '0') {
            return;
        }

        // 将当前位置标记为已访问(即将 '1' 变为 '0')
        grid[r][c] = '0';
        
        // 对当前位置的上、下、左、右四个方向进行深度优先搜索
        dfs(grid, r - 1, c);
        dfs(grid, r + 1, c);
        dfs(grid, r, c - 1);
        dfs(grid, r, c + 1);
    }

    // 主函数,用于计算岛屿数量
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }

        int nr = grid.length;  // 获取网格的行数
        int nc = grid[0].length;  // 获取网格的列数
        int num_islands = 0;  // 初始化岛屿数量为 0

        // 遍历整个网格
        for (int r = 0; r < nr; ++r) {
            for (int c = 0; c < nc; ++c) {
                // 如果当前位置是陆地
                if (grid[r][c] == '1') {
                    ++num_islands;  // 岛屿数量加 1
                    dfs(grid, r, c);  // 对当前岛屿进行深度优先搜索,将所有相邻的陆地标记为已访问
                }
            }
        }

        return num_islands;  // 返回岛屿数量
    }
}

java 复制代码
class Solution {
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;  // 如果网格为空或者行数为0,则返回岛屿数量为0
        }

        int nr = grid.length;  // 获取网格的行数
        int nc = grid[0].length;  // 获取网格的列数
        int num_islands = 0;  // 初始化岛屿数量为0

        for (int r = 0; r < nr; ++r) {  // 遍历网格的每一行
            for (int c = 0; c < nc; ++c) {  // 遍历网格的每一列
                if (grid[r][c] == '1') {  // 如果当前位置是陆地
                    ++num_islands;  // 岛屿数量加1
                    grid[r][c] = '0';  // 将当前位置标记为已访问
                    Queue<Integer> neighbors = new LinkedList<>();  // 创建一个队列用于存储相邻的陆地位置
                    neighbors.add(r * nc + c);  // 将当前位置加入队列
                    while (!neighbors.isEmpty()) {  // 当队列不为空时进行循环
                        int id = neighbors.remove();  // 从队列中取出一个位置
                        int row = id / nc;  // 计算取出位置的行坐标
                        int col = id % nc;  // 计算取出位置的列坐标
                        // 对当前位置的上、下、左、右四个方向进行判断
                        if (row - 1 >= 0 && grid[row-1][col] == '1') {
                            neighbors.add((row-1) * nc + col);  // 将相邻的陆地位置加入队列
                            grid[row-1][col] = '0';  // 将相邻的陆地位置标记为已访问
                        }
                        // 同上
                        if (row + 1 < nr && grid[row+1][col] == '1') {
                            neighbors.add((row+1) * nc + col);
                            grid[row+1][col] = '0';
                        }
                        // 同上
                        if (col - 1 >= 0 && grid[row][col-1] == '1') {
                            neighbors.add(row * nc + col-1);
                            grid[row][col-1] = '0';
                        }
                        // 同上
                        if (col + 1 < nc && grid[row][col+1] == '1') {
                            neighbors.add(row * nc + col+1);
                            grid[row][col+1] = '0';
                        }
                    }
                }
            }
        }

        return num_islands;  // 返回岛屿数量
    }
}

觉得有用的话可以点点赞,支持一下。

如果愿意的话关注一下。会对你有更多的帮助。

每天都会不定时更新哦 >人< 。

相关推荐
1 9 J19 分钟前
Java 上机实践4(类与对象)
java·开发语言·算法
passer__jw7672 小时前
【LeetCode】【算法】3. 无重复字符的最长子串
算法·leetcode
passer__jw7672 小时前
【LeetCode】【算法】21. 合并两个有序链表
算法·leetcode·链表
sweetheart7-72 小时前
LeetCode22. 括号生成(2024冬季每日一题 2)
算法·深度优先·力扣·dfs·左右括号匹配
__AtYou__3 小时前
Golang | Leetcode Golang题解之第557题反转字符串中的单词III
leetcode·golang·题解
李元豪3 小时前
【智鹿空间】c++实现了一个简单的链表数据结构 MyList,其中包含基本的 Get 和 Modify 操作,
数据结构·c++·链表
2401_858286113 小时前
L7.【LeetCode笔记】相交链表
笔记·leetcode·链表
我不是星海3 小时前
1.集合体系补充(1)
java·数据结构
UestcXiye4 小时前
《TCP/IP网络编程》学习笔记 | Chapter 9:套接字的多种可选项
c++·计算机网络·ip·tcp
一丝晨光5 小时前
编译器、IDE对C/C++新标准的支持
c语言·开发语言·c++·ide·msvc·visual studio·gcc