步骤1:问题性质定义
给定一个 m x n
的矩阵 board
,矩阵由字符 'X'
和 'O'
组成。我们需要捕获所有被 'X'
围绕的 'O'
区域,即将这些 'O'
替换为 'X'
。被围绕的 'O'
区域是指这些 'O'
区域完全被 'X'
单元格包围,且该区域不与边缘相连。
输入条件:
board
为一个二维矩阵,包含字符'X'
和'O'
。m
和n
为矩阵的行和列,满足1 <= m, n <= 200
。board[i][j]
的值为'X'
或'O'
。
输出条件:
- 返回一个二维矩阵,其中所有被
'X'
围绕的'O'
被替换为'X'
,其余的'O'
保持不变。
限制:
m
和n
的最大值是 200,因此矩阵的最大大小为 40,000 元素,算法需要在合理的时间复杂度内处理。
边界条件:
board
中只有'X'
或'O'
。board
的尺寸非常小,例如1x1
。- 边缘上的
'O'
不会被替换。
步骤2:解题思路与步骤分解
问题分析:
我们需要标识出哪些 'O'
是被围绕的。显然,边缘上的 'O'
不会被围绕。最简单的办法是先从边缘的 'O'
开始,使用深度优先搜索(DFS)或广度优先搜索(BFS)将与边缘相连的 'O'
标记为"安全",剩下的 'O'
就是被围绕的区域,可以被替换为 'X'
。
解决方案:
- 标记安全区域 :
- 从四个边缘开始遍历所有
'O'
,如果发现'O'
,就用 DFS 或 BFS 将其与相连的'O'
都标记为"安全"。
- 从四个边缘开始遍历所有
- 捕获被围绕的区域 :
- 遍历整个矩阵,对于每个没有被标记为"安全"的
'O'
,将其替换为'X'
。 - 对于标记为"安全"的
'O'
,保持不变。
- 遍历整个矩阵,对于每个没有被标记为"安全"的
算法:
- DFS/BFS :从四个边缘的
'O'
出发,标记所有连接的'O'
。 - 时间复杂度 :
O(m * n)
,每个单元格最多访问一次。 - 空间复杂度 :
O(m * n)
,用于存储标记和递归栈(DFS)或队列(BFS)。
步骤3:C++代码实现
cpp
class Solution {
public:
// DFS实现
void dfs(vector<vector<char>>& board, int i, int j) {
// 边界条件检查
if (i < 0 || j < 0 || i >= board. size() || j >= board[0].size() || board[i][j] != 'O') {
return;
}
// 标记当前'O'为安全
board[i][j] = 'S'; // 'S'表示安全的'O'
// 向四个方向进行DFS
dfs(board, i + 1, j); // 向下
dfs(board, i - 1, j); // 向上
dfs(board, i, j + 1); // 向右
dfs(board, i, j - 1); // 向左
}
void solve(vector<vector<char>>& board) {
if (board. empty() || board[0].empty()) return;
int m = board.size();
int n = board[0].size();
// 从四个边缘开始DFS
// 遍历第一行和最后一行
for (int j = 0; j < n; j++) {
if (board[0][j] == 'O') dfs(board, 0, j); // 第一行
if (board[m-1][j] == 'O') dfs(board, m-1, j); // 最后一行
}
// 遍历第一列和最后一列
for (int i = 0; i < m; i++) {
if (board[i][0] == 'O') dfs(board, i, 0); // 第一列
if (board[i][n-1] == 'O') dfs(board, i, n-1); // 最后一列
}
// 修改被围绕的'O'为'X', 将安全的'O'恢复为'O'
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (board[i][j] == 'O') {
board[i][j] = 'X'; // 被围绕的'O'替换为'X'
}
else if (board[i][j] == 'S') {
board[i][j] = 'O'; // 安全的'O'恢复为'O'
}
}
}
}
};
代码注释:
- DFS函数 :用于深度优先搜索,找到与边缘相连的
'O'
,并将它们标记为'S'
(安全)。 - 捕获函数
capture
:首先从矩阵的四个边缘开始,找到所有与边缘相连的'O'
,标记为安全。然后遍历整个矩阵,捕获所有被'X'
围绕的'O'
,并替换为'X'
。 - 主函数 :初始化一个示例
board
,调用capture
函数进行处理,最后输出结果。
步骤4:启发
通过这个问题,我们可以学到以下几个启发:
- 区域标记技术:通过遍历矩阵边缘,将与边缘相连的区域标记为特殊状态(如'S'),然后对其他区域进行处理。这种方法在处理图形或网格类问题时非常常见。
- DFS/BFS应用:DFS 和 BFS 是解决图形问题的常见算法,能够有效标记与特定节点相连的区域,避免重复计算。
- 边界条件处理:在解决矩阵问题时,需要特别注意边界条件,确保不会越界或错漏。
步骤5:实际应用
这个算法在很多实际应用中有用,尤其是在图像处理、区域划分和边界检测等领域。以下是一个实际应用示例:
应用示例:图像边界检测与区域填充
在图像处理中,我们可能需要处理图像中的封闭区域,例如通过填充算法填充一个被边界围住的区域。类似于这个问题,图像中可能存在"被围绕"的像素区域(例如,某些区域需要被颜色填充,而不想影响边缘区域)。此时,可以使用类似的 DFS/BFS 技术从边缘开始扫描,标记不需要填充的区域,最后进行填充。
实现方法:
- 对于二值图像(0表示背景,1表示前景),从图像的边缘开始,找到所有与背景相连的前景像素。
- 将所有与边缘相连的前景像素标记为"安全"。
- 将剩余的前景像素填充为背景颜色。
这种方法被广泛应用于图像编辑软件中,用于实现区域填充、图像剪裁等功能。