Given a 2D board containing 'X'
and 'O'
(the letter O), capture all regions surrounded by 'X'
.
A region is captured by flipping all 'O'
s into 'X'
s in that surrounded region.
Example:
X X X X
X O O X
X X O X
X O X X
After running your function, the board should be:
X X X X
X X X X
X X X X
X O X X
Explanation:
Surrounded regions shouldn’t be on the border, which means that any 'O'
on the border of the board are not flipped to 'X'
. Any 'O'
that is not on the border and it is not connected to an 'O'
on the border will be flipped to 'X'
. Two cells are connected if they are adjacent cells connected horizontally or vertically.
这是道关于 XXOO 的题,有点像围棋,将包住的O都变成X,但不同的是边缘的O不算被包围,跟之前那道 Number of Islands 很类似,都可以用 DFS 来解。刚开始我的思路是 DFS 遍历中间的O,如果没有到达边缘,都变成X,如果到达了边缘,将之前变成X的再变回来。但是这样做非常的不方便,在网上看到大家普遍的做法是扫矩阵的四条边,如果有O,则用 DFS 遍历,将所有连着的O都变成另一个字符,比如 \$,这样剩下的O都是被包围的,然后将这些O变成X,把$变回O就行了。代码如下:
解法一:
class Solution {
public:
void solve(vector<vector<char> >& board) {
for (int i = ; i < board.size(); ++i) {
for (int j = ; j < board[i].size(); ++j) {
if ((i == || i == board.size() - || j == || j == board[i].size() - ) && board[i][j] == 'O')
solveDFS(board, i, j);
}
}
for (int i = ; i < board.size(); ++i) {
for (int j = ; j < board[i].size(); ++j) {
if (board[i][j] == 'O') board[i][j] = 'X';
if (board[i][j] == '$') board[i][j] = 'O';
}
}
}
void solveDFS(vector<vector<char> > &board, int i, int j) {
if (board[i][j] == 'O') {
board[i][j] = '$';
if (i > && board[i - ][j] == 'O')
solveDFS(board, i - , j);
if (j < board[i].size() - && board[i][j + ] == 'O')
solveDFS(board, i, j + );
if (i < board.size() - && board[i + ][j] == 'O')
solveDFS(board, i + , j);
if (j > && board[i][j - ] == 'O')
solveDFS(board, i, j - );
}
}
};
很久以前,上面的代码中最后一个 if 中必须是 j > 1 而不是 j > 0,为啥 j > 0 无法通过 OJ 的最后一个大数据集合,博主开始也不知道其中奥秘,直到被另一个网友提醒在本地机子上可以通过最后一个大数据集合,于是博主也写了一个程序来验证,请参见验证 LeetCode Surrounded Regions 包围区域的DFS方法,发现 j > 0 是正确的,可以得到相同的结果。神奇的是,现在用 j > 0 也可以通过 OJ 了。
下面这种解法还是 DFS 解法,只是递归函数的写法稍有不同,但是本质上并没有太大的区别,参见代码如下:
解法二:
class Solution {
public:
void solve(vector<vector<char>>& board) {
if (board.empty() || board[].empty()) return;
int m = board.size(), n = board[].size();
for (int i = ; i < m; ++i) {
for (int j = ; j < n; ++j) {
if (i == || i == m - || j == || j == n - ) {
if (board[i][j] == 'O') dfs(board, i , j);
}
}
}
for (int i = ; i < m; ++i) {
for (int j = ; j < n; ++j) {
if (board[i][j] == 'O') board[i][j] = 'X';
if (board[i][j] == '$') board[i][j] = 'O';
}
}
}
void dfs(vector<vector<char>> &board, int x, int y) {
int m = board.size(), n = board[].size();
vector<vector<int>> dir{{,-},{-,},{,},{,}};
board[x][y] = '$';
for (int i = ; i < dir.size(); ++i) {
int dx = x + dir[i][], dy = y + dir[i][];
if (dx >= && dx < m && dy > && dy < n && board[dx][dy] == 'O') {
dfs(board, dx, dy);
}
}
}
};
我们也可以使用迭代的解法,但是整体的思路还是一样的,在找到边界上的O后,然后利用队列 queue 进行 BFS 查找和其相连的所有O,然后都标记上美元号。最后的处理还是先把所有的O变成X,然后再把美元号变回O即可,参见代码如下:
解法三:
class Solution {
public:
void solve(vector<vector<char>>& board) {
if (board.empty() || board[].empty()) return;
int m = board.size(), n = board[].size();
for (int i = ; i < m; ++i) {
for (int j = ; j < n; ++j) {
if (i != && i != m - && j != && j != n - ) continue;
if (board[i][j] != 'O') continue;
board[i][j] = '$';
queue<int> q{{i * n + j}};
while (!q.empty()) {
int t = q.front(), x = t / n, y = t % n; q.pop();
if (x >= && board[x - ][y] == 'O') {board[x - ][y] = '$'; q.push(t - n);}
if (x < m - && board[x + ][y] == 'O') {board[x + ][y] = '$'; q.push(t + n);}
if (y >= && board[x][y - ] == 'O') {board[x][y - ] = '$'; q.push(t - );}
if (y < n - && board[x][y + ] == 'O') {board[x][y + ] = '$'; q.push(t + );}
}
}
}
for (int i = ; i < m; ++i) {
for (int j = ; j < n; ++j) {
if (board[i][j] == 'O') board[i][j] = 'X';
if (board[i][j] == '$') board[i][j] = 'O';
}
}
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/130
类似题目:
参考资料:
https://leetcode.com/problems/surrounded-regions/
https://leetcode.com/problems/surrounded-regions/discuss/41895/Share-my-clean-Java-Code