给定一个二维的矩阵,包含 ‘X’ 和 ‘O’(字母 O)。
找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。
示例:
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
#include <vector>
#include <map>
#include <set>
using namespace std;
const int DirectionX[4] = { 0 , 0 , 1 , -1 };
const int DirectionY[4] = { 1 , -1 , 0 , 0 };
class Solution
{
//方法1:广搜/(深搜)
//1、首选BFS广搜矩阵边缘的数据,标记处与边缘'O'相连的所有'O',使用'.'标记
//2、遍历矩阵,将所有非'.'的修改为'X',将所有'.'的修改为'O'
public:
void solve_1(vector<vector<char> >& board)
{
if (board.empty())return;
//遍历左右边界,从上到下
for (int i = 0; i < board.size(); ++i)
{
BFS(board, i, 0);
BFS(board, i, board[0].size() - 1);
}
//遍历上下边界,从左到右
for (int j = 0; j < board[0].size(); ++j)
{
BFS(board, 0, j);
BFS(board, board.size() - 1, j);
}
//替换字符
for (int i = 0; i < board.size(); i++)
for (int j = 0; j < board[i].size(); j++)
if (board[i][j] == '.')
board[i][j] = 'O';
else board[i][j] = 'X';
}
private:
void BFS(vector<vector<char> > & board, int x, int y) // 注意对board进行传引用,不然形参board的修改无法传到实参中
{
//坐标(x,y)是否在矩阵内
//坐标(x,y)字符是否是'O'
if (x >= 0 && x < board.size() && y >= 0 && y < board[x].size()
&& board[x][y] == 'O')
{
board[x][y] = '.';
for (int index = 0; index < 4; index++)
BFS(board, x + DirectionX[index], y + DirectionY[index]);
}
}
//--------------------------------------------------------------
//方法2: 并查集(测试发现效率并不高,但如果数据量超级大的话,效率会比深搜广搜高吧)
//1、使用并查集合并所有相连接的‘O’,只需遍历一遍即可
//2、记录边界的‘O’的坐标在集合中的集合ID,存在set<pair<int, int>> boundOSet
//3、遍历非边界的所有‘O’,使用set<pair<int, int>> boundOSet.find()判断‘O’是否和边界的‘O’在同一个集合
public:
void solve(vector<vector<char> > & board)
{
if (board.empty())return;
//并查集,将相邻的'O'链接成一个集合
DisJointSet disJointSet(board);
for (int i = 0; i < board.size(); ++i)
for (int j = 0; j < board[i].size(); ++j) {
if (board[i][j] != 'O')continue;
for (int index = 0; index < 4; ++index)
{
int newI = i + DirectionX[index];
int newJ = j + DirectionY[index];
if (newI < 0 || newJ < 0 || newI >= board.size() || newJ >= board[i].size())continue;
if (board[newI][newJ] == 'O')
disJointSet.Union(make_pair(i, j), make_pair(newI, newJ));
}
}
//记录边界上有'O'的坐标 在并查集中的 ID
set<pair<int, int>> boundOSet;
//遍历左右边界,从上到下
for (int i = 0; i < board.size(); ++i)
{
if (board[i][0] == 'O')
boundOSet.insert(disJointSet.Find(make_pair(i, 0)));
if (board[i][board[0].size() - 1] == 'O')
boundOSet.insert(disJointSet.Find(make_pair(i, board[0].size() - 1)));
}
//遍历上下边界,从左到右
for (int j = 0; j < board[0].size(); ++j)
{
if (board[0][j] == 'O')
boundOSet.insert(disJointSet.Find(make_pair(0, j)));
if (board[board.size() - 1][j] == 'O')
boundOSet.insert(disJointSet.Find(make_pair(board.size() - 1, j)));
}
//遍历非边缘的'O',判断与边缘'O'是否是一个集合
for (int i = 1; i < board.size() - 1; ++i)
for (int j = 1; j < board[i].size() - 1; ++j)
if (board[i][j] == 'O')
if (boundOSet.find(disJointSet.Find(make_pair(i, j))) == boundOSet.end())
board[i][j] = 'X';
}
};
//并查集类
class DisJointSet
{
public:
DisJointSet(const vector<vector<char> >& board) {
for (int i = 0; i < board.size(); ++i)
for (int j = 0; j < board[i].size(); ++j) {
_coordSet[make_pair(i, j)] = make_pair(i, j);
_count[make_pair(i, j)] = 1;
}
}
pair<int, int> Find(pair<int, int> coord) {
while (_coordSet[coord] != coord)
{
_coordSet[coord] = _coordSet[_coordSet[coord]];
coord = _coordSet[coord];
}
return coord;
}
void Union(pair<int, int> coord1, pair<int, int> coord2) {
auto&& ID1 = Find(coord1);
auto&& ID2 = Find(coord2);
if (ID1 == ID2)return;
if (_count[ID1] < _count[ID2]) {
_coordSet[ID1] = ID2;
_count[ID2] += _count[ID1];
}
else {
_coordSet[ID2] = ID1;
_count[ID1] += _count[ID2];
}
return;
}
private:
map<pair<int, int>, pair<int, int>> _coordSet;//(坐标,集合ID(以坐标为值))
map<pair<int, int>, int> _count; //(集合ID(以坐标为值),个数)
};