Leetcode 152 被围绕的区域 | 岛屿数量

1 题目

130. 被围绕的区域

给你一个 m x n 的矩阵 board ,由若干字符 'X''O' 组成,捕获 所有 被围绕的区域

  • **连接:**一个单元格与水平或垂直方向上相邻的单元格连接。
  • 区域:连接所有 'O' 的单元格来形成一个区域。
  • 围绕: 如果一个区域中的所有 'O' 单元格都不在棋盘的边缘,则该区域被包围。这样的区域 完全'X' 单元格包围。

通过 原地 将输入矩阵中的所有 'O' 替换为 'X'捕获被围绕的区域。你不需要返回任何值。

示例 1:

**输入:**board = [['X','X','X','X'],['X','O','O','X'],['X','X','O','X'],['X','O','X','X']]

输出:[['X','X','X','X'],['X','X','X','X'],['X','X','X','X'],['X','O','X','X']]

解释:

在上图中,底部的区域没有被捕获,因为它在 board 的边缘并且不能被围绕。

示例 2:

**输入:**board = [['X']]

输出:[['X']]

提示:

  • m == board.length
  • n == board[i].length
  • 1 <= m, n <= 200
  • board[i][j]'X''O'

2 代码实现

c++

cpp 复制代码
class Solution {
public:
    void solve(vector<vector<char>>& board) {
        if (board.empty() || board[0].empty()) return ;
        int m = board.size();
        int n = board[0].size();

        for (int i = 0 ; i < m ; i ++){
            if (board[i][0] == 'O') dfs(board, i, 0 , m , n);
            if (board[i][n -1 ] == 'O') dfs(board,i , n -1 , m , n);
        }
        for (int j = 0 ; j < n ; j++){
            if (board[0][j] == 'O') dfs(board, 0, j, m, n);
            if (board[m-1][j] == 'O') dfs(board, m-1, j, m, n);
        }
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (board[i][j] == 'A') board[i][j] = 'O';
                else if (board[i][j] == 'O') board[i][j] = 'X';
            }
        }
    }
private:
    void dfs(vector<vector<char>>& board, int i, int j, int m, int n) {
        if (i < 0 || i >= m || j < 0 || j >= n || board[i][j] != 'O') {
            return;
        }

        board[i][j] = 'A';

        dfs(board, i - 1, j, m, n); 
        dfs(board, i + 1, j, m, n); 
        dfs(board, i, j - 1, m, n); 
        dfs(board, i, j + 1, m, n); 
    }
};

js

javascript 复制代码
/**
 * @param {character[][]} board
 * @return {void} Do not return anything, modify board in-place instead.
 */
var solve = function(board) {
    if (!board || board.length === 0) return;
    const m = board.length;    
    const n = board[0].length; 

    for (let i = 0; i < m; i++) {
        if (board[i][0] === 'O') dfs(board, i, 0, m, n);
        if (board[i][n-1] === 'O') dfs(board, i, n-1, m, n);
    }
    for (let j = 0; j < n; j++) {
        if (board[0][j] === 'O') dfs(board, 0, j, m, n);
        if (board[m-1][j] === 'O') dfs(board, m-1, j, m, n);
    }

    for (let i = 0; i < m; i++) {
        for (let j = 0; j < n; j++) {
            if (board[i][j] === 'A') board[i][j] = 'O';
            else if (board[i][j] === 'O') board[i][j] = 'X';
        }
    }
};


function dfs(board, i, j, m, n) {
    if (i < 0 || i >= m || j < 0 || j >= n || board[i][j] !== 'O') {
        return;
    }
    board[i][j] = 'A';
    dfs(board, i - 1, j, m, n);
    dfs(board, i + 1, j, m, n);
    dfs(board, i, j - 1, m, n);
    dfs(board, i, j + 1, m, n);
}

思考

这什么其实我一点都不知道该怎么做,我是想到之前有DFS , BFS ,动态规划。但是放在图里面要怎么做呢?

题解

题目要求:

  1. 被 X 完全包围 的所有 O 变成 X
  2. 边界上的 O 永远不会被包围,不能变
  3. 和边界 O 连通的 O 也不会被包围,不能变
  4. 剩下的内部孤立 O 才需要变成 X

反向思维

  1. 先找到所有边界上的 O
  2. 用 DFS / BFS 把和边界 O 连通的所有 O 标记出来 (比如改成临时字符 A
  3. 最后遍历矩阵:
    • 标记为 A → 恢复成 O(安全,不包围)
    • 还是 O → 变成 X(被包围)

这道题本质是二维网格的连通性搜索,两种方法都可以:

  1. DFS(深度优先搜索):代码简洁,递归实现
  2. BFS(广度优先搜索):非递归,用队列,避免栈溢出

DFS

cpp 复制代码
#include <vector>
using namespace std;

class Solution {
public:
    void solve(vector<vector<char>>& board) {
        if (board.empty() || board[0].empty()) return;
        int m = board.size();    // 行数
        int n = board[0].size(); // 列数

        // 1. 遍历 4 条边界,把边界 O 及其连通 O 标记为 A
        for (int i = 0; i < m; i++) {
            // 第一列
            if (board[i][0] == 'O') dfs(board, i, 0, m, n);
            // 最后一列
            if (board[i][n-1] == 'O') dfs(board, i, n-1, m, n);
        }
        for (int j = 0; j < n; j++) {
            // 第一行
            if (board[0][j] == 'O') dfs(board, 0, j, m, n);
            // 最后一行
            if (board[m-1][j] == 'O') dfs(board, m-1, j, m, n);
        }

        // 2. 遍历整个矩阵,替换字符
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 标记 A = 安全 O,恢复
                if (board[i][j] == 'A') board[i][j] = 'O';
                // 剩下的 O = 被包围,改成 X
                else if (board[i][j] == 'O') board[i][j] = 'X';
            }
        }
    }

private:
    // DFS 递归函数:把当前 O 标记为 A,并向上下左右搜索
    void dfs(vector<vector<char>>& board, int i, int j, int m, int n) {
        // 越界 或 不是 O,直接返回
        if (i < 0 || i >= m || j < 0 || j >= n || board[i][j] != 'O') {
            return;
        }

        // 标记为临时字符 A
        board[i][j] = 'A';

        // 上下左右四个方向递归搜索
        dfs(board, i - 1, j, m, n); // 上
        dfs(board, i + 1, j, m, n); // 下
        dfs(board, i, j - 1, m, n); // 左
        dfs(board, i, j + 1, m, n); // 右
    }
};
  1. 边界判断:矩阵为空直接返回
  2. 遍历四条边
    • 第一列、最后一列、第一行、最后一行
    • 遇到 O 就启动 DFS
  3. DFS 函数
    • 先判断越界 + 是否是 O
    • 是 O 就标记成 A(表示安全)
    • 递归上下左右继续找连通的 O
  4. 最终替换
    • AO
    • 剩余 OX

如果矩阵特别大,DFS 递归可能栈溢出,BFS 更安全。

BFS

cpp 复制代码
#include <vector>
#include <queue>
using namespace std;

class Solution {
public:
    // 方向数组:上下左右
    int dirs[4][2] = {{-1,0}, {1,0}, {0,-1}, {0,1}};

    void solve(vector<vector<char>>& board) {
        if (board.empty()) return;
        int m = board.size(), n = board[0].size();
        queue<pair<int, int>> q;

        // 1. 把所有边界 O 入队
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 只处理边界上的 O
                if ((i == 0 || i == m-1 || j == 0 || j == n-1) && board[i][j] == 'O') {
                    q.push({i, j});
                    board[i][j] = 'A';
                }
            }
        }

        // 2. BFS 扩散标记
        while (!q.empty()) {
            auto [x, y] = q.front();
            q.pop();

            // 遍历四个方向
            for (auto& dir : dirs) {
                int nx = x + dir[0];
                int ny = y + dir[1];
                // 合法且是 O,就标记并入队
                if (nx >= 0 && nx < m && ny >=0 && ny < n && board[nx][ny] == 'O') {
                    board[nx][ny] = 'A';
                    q.push({nx, ny});
                }
            }
        }

        // 3. 最终替换
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (board[i][j] == 'A') board[i][j] = 'O';
                else if (board[i][j] == 'O') board[i][j] = 'X';
            }
        }
    }
};

运行流程演示

输入矩阵:

复制代码
X X X X
X O O X
X X O X
X O X X
  1. 找到边界 O :只有 (3,1) 那个 O
  2. DFS/BFS 标记它为 A
  3. 其他内部 O 没有被标记
  4. 最终替换:
    • 标记 A → O
    • 其他 O → X

核心要点

  1. 反向思维 :不找被包围的,找不被包围的
  2. 边界 O 是安全区,连通的 O 都安全
  3. 临时字符 A 做标记,避免重复遍历
  4. 搜索只用上下左右四个方向

总结

  1. 这道题核心是反向标记,不是直接找被包围区域
  2. DFS 代码简洁好写,适合面试
  3. BFS 非递归,适合大数据
  4. 时间复杂度:O(m×n)(每个格子只访问一次)
  5. 空间复杂度:O(m×n)(递归栈 / 队列最大空间)

3 题目

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'

4 代码实现

c++

cpp 复制代码
class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        if (grid.empty() || grid[0].empty()) return 0;
        
        int m = grid.size();    
        int n = grid[0].size(); 
        int count = 0;         


        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == '1') {
                    count++; 
                    dfs(grid, i, j, m, n);
                }
            }
        }
        return count;
    }

private:
    void dfs(vector<vector<char>>& grid, int i, int j, int m, int n) {
        if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] == '0') {
            return;
        }

        grid[i][j] = '0';

        dfs(grid, i - 1, j, m, n); 
        dfs(grid, i + 1, j, m, n); 
        dfs(grid, i, j - 1, m, n); 
        dfs(grid, i, j + 1, m, n); 
    }
};

js

javascript 复制代码
/**
 * @param {character[][]} grid
 * @return {number}
 */
var numIslands = function(grid) {
    if (!grid || grid.length === 0) return 0;
    const m = grid.length;
    const n = grid[0].length;
    let count = 0;

    for (let i = 0; i < m; i++) {
        for (let j = 0; j < n; j++) {
            if (grid[i][j] === '1') {
                count++;
                dfs(grid, i, j, m, n);
            }
        }
    }
    return count;
};

function dfs(grid, i, j, m, n) {
    if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] === '0') {
        return;
    }

    grid[i][j] = '0';

    dfs(grid, i - 1, j, m, n);
    dfs(grid, i + 1, j, m, n);
    dfs(grid, i, j - 1, m, n);
    dfs(grid, i, j + 1, m, n);
}

思考

诶,我咋整,我怎么知道是不是连续的?

题解

这道题和刚才的被围绕的区域是一模一样的思路,学会这道,图的 DFS/BFS 你就彻底通了!

一、先搞懂:什么是岛屿?

  • 1 = 陆地0 = 海洋
  • 上下左右连在一起的 1 = 1 个岛屿
  • 只要不连在一起,就是新的岛屿

二、核心思路(超级简单)

  1. 遍历整个网格
  2. 遇到 1 → 说明找到一个新岛屿计数 +1
  3. 马上用 DFS / BFS这块陆地所有连在一起的 1 全部淹掉(变成 0)
  4. 继续遍历,重复上面步骤

👉 为什么要淹掉? 避免重复计数!已经算过的岛屿,就别再碰了。


三、DFS 解法(代码最短,面试首选)

cpp 复制代码
#include <vector>
using namespace std;

class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        if (grid.empty() || grid[0].empty()) return 0;
        
        int m = grid.size();    // 行数
        int n = grid[0].size(); // 列数
        int count = 0;         // 岛屿数量

        // 1. 遍历每一个格子
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 2. 遇到陆地 1,发现新岛屿!
                if (grid[i][j] == '1') {
                    count++; // 计数+1
                    dfs(grid, i, j, m, n); // 3. DFS 把整个岛屿淹成 0
                }
            }
        }
        return count;
    }

private:
    // DFS:把当前及相连的所有 1 都变成 0(淹掉)
    void dfs(vector<vector<char>>& grid, int i, int j, int m, int n) {
        // 越界 或 不是陆地,直接返回
        if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] == '0') {
            return;
        }

        // 把当前陆地淹掉:1 → 0
        grid[i][j] = '0';

        // 上下左右 继续淹!
        dfs(grid, i - 1, j, m, n); // 上
        dfs(grid, i + 1, j, m, n); // 下
        dfs(grid, i, j - 1, m, n); // 左
        dfs(grid, i, j + 1, m, n); // 右
    }
};

四、BFS 解法(非递归,不爆栈)

cpp 复制代码
#include <vector>
#include <queue>
using namespace std;

class Solution {
public:
    // 上下左右四个方向
    int dirs[4][2] = {{-1,0}, {1,0}, {0,-1}, {0,1}};

    int numIslands(vector<vector<char>>& grid) {
        if (grid.empty()) return 0;
        int m = grid.size(), n = grid[0].size();
        int count = 0;
        queue<pair<int, int>> q;

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 发现新岛屿
                if (grid[i][j] == '1') {
                    count++;
                    q.push({i, j});
                    grid[i][j] = '0'; // 淹掉

                    // BFS 扩散淹掉整个岛屿
                    while (!q.empty()) {
                        auto [x, y] = q.front();
                        q.pop();

                        // 遍历四个方向
                        for (auto& dir : dirs) {
                            int nx = x + dir[0];
                            int ny = y + dir[1];
                            if (nx >= 0 && nx < m && ny >=0 && ny < n && grid[nx][ny] == '1') {
                                grid[nx][ny] = '0';
                                q.push({nx, ny});
                            }
                        }
                    }
                }
            }
        }
        return count;
    }
};

五、怎么知道是不是连续的?

DFS/BFS 就是专门找 "连续 / 连通" 的工具!

  • 你找到一个 1
  • 让代码自动往上下左右找
  • 找到就淹成 0,再也不会重复算
  • 这样每找到一次 1,就是一个全新的岛屿

六、运行流程

复制代码
1 1 0 0 0
1 1 0 0 0
0 0 1 0 0
0 0 0 1 1
  1. 遍历到 左上角 1 → 计数 = 1,淹掉一大片 1
  2. 遍历到 中间 1 → 计数 = 2,淹掉
  3. 遍历到 右下角 1 → 计数 = 3,淹掉
  4. 最终输出 3,完美!

七、和上一题对比(你会发现一模一样)

题目 核心动作 目的
130 被围绕的区域 找到边界 O → 标记 A 保护安全 O
200 岛屿数量 找到 1 → 淹成 0 避免重复计数

👉 本质:二维网格 + 上下左右搜索 + DFS/BFS 学会这两道,LeetCode 图的搜索题你直接通杀!


总结

  1. 遇到 1 = 新岛屿,计数 +1
  2. DFS/BFS 淹掉整个岛屿,防止重复
  3. 时间复杂度:O(m×n)(每个格子只走一次)
  4. 空间复杂度:O(m×n)(递归栈 / 队列)

5 小结

眼睛会了,代码还没自己能默出来,看明白了第一个dfs的思路,但是具体怎么实现的细节自己要重新写写,也要看看bfs怎么写。

有点仓促,相信睡梦中能消化一下!()

坚持,加油(ง •_•)ง!!!

相关推荐
吕司2 小时前
LeetCode Hot Code——最大子数组和
数据结构·算法·leetcode
海清河晏1112 小时前
数据结构 | 单链表
数据结构·unity·dreamweaver
XiYang-DING2 小时前
【LeetCode】144. 二叉树的前序遍历
算法·leetcode·职场和发展
WolfGang0073212 小时前
代码随想录算法训练营 Day28 | 动态规划 part01
算法·动态规划
光电笑映2 小时前
STL 源码解密:unordered 系列容器的底层复用与哈希策略
算法·哈希算法·散列表
6Hzlia2 小时前
【Hot 100 刷题计划】 LeetCode 215. 数组中的第K个最大元素 | C++ 快速选择与堆排序题解
c++·算法·leetcode
小白菜又菜2 小时前
Leetcode 3070. Count Submatrices with Top-Left Element and Sum Less Than k
算法·leetcode·职场和发展
笨笨饿3 小时前
32_复变函数在工程中实际应用区别于联系
linux·服务器·c语言·人工智能·单片机·算法·学习方法
会编程的土豆3 小时前
【数据结构与算法】拓扑排序2
数据结构·算法·leetcode