1 题目
给你一个 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.lengthn == board[i].length1 <= m, n <= 200board[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 ,动态规划。但是放在图里面要怎么做呢?
题解
题目要求:
- 把被 X 完全包围 的所有
O变成X - 边界上的 O 永远不会被包围,不能变
- 和边界 O 连通的 O 也不会被包围,不能变
- 剩下的内部孤立 O 才需要变成 X
反向思维:
- 先找到所有边界上的 O
- 用 DFS / BFS 把和边界 O 连通的所有 O 标记出来 (比如改成临时字符
A) - 最后遍历矩阵:
- 标记为
A→ 恢复成O(安全,不包围) - 还是
O→ 变成X(被包围)
- 标记为
这道题本质是二维网格的连通性搜索,两种方法都可以:
- DFS(深度优先搜索):代码简洁,递归实现
- 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); // 右
}
};
- 边界判断:矩阵为空直接返回
- 遍历四条边 :
- 第一列、最后一列、第一行、最后一行
- 遇到
O就启动 DFS
- DFS 函数 :
- 先判断越界 + 是否是 O
- 是 O 就标记成
A(表示安全) - 递归上下左右继续找连通的 O
- 最终替换 :
A→O- 剩余
O→X
如果矩阵特别大,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
- 找到边界 O :只有
(3,1)那个 O - DFS/BFS 标记它为
A - 其他内部 O 没有被标记
- 最终替换:
- 标记 A → O
- 其他 O → X
核心要点
- 反向思维 :不找被包围的,找不被包围的
- 边界 O 是安全区,连通的 O 都安全
- 用临时字符 A 做标记,避免重复遍历
- 搜索只用上下左右四个方向
总结
- 这道题核心是反向标记,不是直接找被包围区域
- DFS 代码简洁好写,适合面试
- BFS 非递归,适合大数据
- 时间复杂度:O(m×n)(每个格子只访问一次)
- 空间复杂度:O(m×n)(递归栈 / 队列最大空间)
3 题目
给你一个由 '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.lengthn == grid[i].length1 <= m, n <= 300grid[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 → 说明找到一个新岛屿 !计数 +1
- 马上用 DFS / BFS 把这块陆地所有连在一起的 1 全部淹掉(变成 0)
- 继续遍历,重复上面步骤
👉 为什么要淹掉? 避免重复计数!已经算过的岛屿,就别再碰了。
三、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 → 计数 = 3,淹掉
- 最终输出 3,完美!
七、和上一题对比(你会发现一模一样)
| 题目 | 核心动作 | 目的 |
|---|---|---|
| 130 被围绕的区域 | 找到边界 O → 标记 A | 保护安全 O |
| 200 岛屿数量 | 找到 1 → 淹成 0 | 避免重复计数 |
👉 本质:二维网格 + 上下左右搜索 + DFS/BFS 学会这两道,LeetCode 图的搜索题你直接通杀!
总结
- 遇到 1 = 新岛屿,计数 +1
- DFS/BFS 淹掉整个岛屿,防止重复
- 时间复杂度:O(m×n)(每个格子只走一次)
- 空间复杂度:O(m×n)(递归栈 / 队列)
5 小结
眼睛会了,代码还没自己能默出来,看明白了第一个dfs的思路,但是具体怎么实现的细节自己要重新写写,也要看看bfs怎么写。
有点仓促,相信睡梦中能消化一下!()
坚持,加油(ง •_•)ง!!!