[LeetCode] 130. Surrounded Regions 包围区域
1.DFS
这道题让将矩阵中被'X'包围的不接触边框的'O组成的连通分量用'X'替换,显然可以使用DFS的方法解答,直接的想法是,遍历二维矩阵,遇到‘O’使用DFS判断由'O'组成的连通分量有没有接触到边框,
没有接触到DFS返回true,并将'O组成的连通分量用'X'替换;否则返回false,不替换。在使用DFS遍历'O'连通分量的过程中使用数组保存遍历路径。此种方法容易出错。代码如下:
/* * @Descripttion: * @version: * @Author: wangxf * @Date: 2020-04-14 20:38:54 * @LastEditors: Do not edit * @LastEditTime: 2020-04-20 23:19:05 */ /* * @lc app=leetcode.cn id=130 lang=cpp * * [130] 被围绕的区域 */ // @lc code=start #include<bits/stdc++.h> using namespace std; class Solution { public: void solve(vector<vector<char>>& board) { if(board.empty()||board.size()<1) return; vector<vector<char>> flag = board; raw_len = board.size(); col_len = board[0].size(); for(int i = 0;i<raw_len;++i) for(int j = 0;j<col_len;++j) { flag[i][j]='N';//未访问 } for(int i = 0;i<raw_len;++i) for(int j = 0;j<col_len;++j) { if(board[i][j]=='O'){ vector<pair<int,int> > pathVec; pathVec.clear(); bool isModify = dfs(board,flag,pathVec,i,j); if(isModify) { vector<pair<int,int> >::iterator ite = pathVec.begin(); for(;ite!=pathVec.end();++ite){ int x = ite->first; int y = ite->second; board[x][y]='X'; } } } } return; } bool dfs(vector<vector<char> >& board,vector<vector<char> >& flag, vector<pair<int,int> >& pathVec,int i,int j) { if(!(i>=0&&i<raw_len&&j>=0&&j<col_len)){//放在函数最开始,避免数组下标越界 return false; } bool status = true; if(board[i][j]=='O'&&flag[i][j]=='N'){//没有被访问过的‘O’节点 flag[i][j]='Y';//标记访问 pair<int,int> point = make_pair(i,j); pathVec.push_back(point);//记录一次dfs过程中的路径 if(i==0||j==0){//遇到边界,不填充 status=false; } int dx[4]={0,0,-1,1}; int dy[4]={1,-1,0,0}; for(int k=0;k<4;++k){ status=(status&dfs(board,flag,pathVec,i+dx[k],j+dy[k])); } return status; } return status; } private: int raw_len; int col_len; }; // @lc code=end
另一种比较巧妙一点的DFS方法是扫矩阵的四条边,如果有O,则用 DFS 遍历,将所有连着的O都变成另一个字符,比如 $,这样剩下的O都是被包围的,然后将这些O变成X,把$变回O就行了。代码如下:
class Solution { public: void solve(vector<vector<char> >& board) { for (int i = 0; i < board.size(); ++i) { for (int j = 0; j < board[i].size(); ++j) { if ((i == 0 || i == board.size() - 1 || j == 0 || j == board[i].size() - 1) && board[i][j] == 'O') solveDFS(board, i, j); } } for (int i = 0; i < board.size(); ++i) { for (int j = 0; 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 > 0 && board[i - 1][j] == 'O') solveDFS(board, i - 1, j); if (j < board[i].size() - 1 && board[i][j + 1] == 'O') solveDFS(board, i, j + 1); if (i < board.size() - 1 && board[i + 1][j] == 'O') solveDFS(board, i + 1, j); if (j > 0 && board[i][j - 1] == 'O') solveDFS(board, i, j - 1); } } };
2.并查集
/* * @Descripttion: * @version: * @Author: wangxf * @Date: 2020-04-14 20:38:54 * @LastEditors: Do not edit * @LastEditTime: 2020-05-07 23:29:17 */ /* * @lc app=leetcode.cn id=130 lang=cpp * * [130] 被围绕的区域 */ // @lc code=start #include<bits/stdc++.h> using namespace std; class UF { private: int count;//连通的个数 vector<int> parent;//保存节点的父节点 vector<int> size;//保存节点所在连通分量的重量(节点数) public: // 构造函数初始化 UF(int n) { count = n; parent.resize(n); size.resize(n); for(int i = 0; i < n; i++) { parent[i] =i; size[i] = 1; } } // 将节点p 和 节点 q 连通 void Union(int p, int q) { int rootP = find(p); int rootQ = find(q); if(rootP == rootQ) return; if(size[rootP] > size[rootQ]) { parent[rootQ] = rootP; size[rootP] += size[rootQ]; } else { parent[rootP] = rootQ; size[rootQ] += size[rootP]; } count--;//连通个数 -1 } //判断节点p 和 节点 q 是否连通 bool connected(int p, int q) { int rootP = find(p); int rootQ = find(q); return rootP==rootQ; } //返回当前连通个数 int count_num() { return count; } private: // 寻找 x 节点的根节点 int find(int x) { while(parent[x] != x) { //查找根节点的同时,对树进行路径压缩, parent[x] = parent[parent[x]]; x = parent[x]; } return x; } }; //并查集常见思路是:适当增加虚拟节点,想办法让元素「分门别类」,建立动态连通关系。 class Solution { public: void solve(vector<vector<char>>& board) { if(board.empty()||board.size()==0) return; const int m = board.size(); const int n = board[0].size(); UF *uf = new UF(m*n+1);//给dummy节点留一个位置 int dummy = m*n; //将首列和末列的‘O’和dummy连通 for(int i = 0;i<m;++i) { if(board[i][0]=='O') uf->Union(n*i+0,dummy); if(board[i][n-1]=='O') uf->Union(n*i+n-1,dummy); } //将首行和末行的‘O’和dummy连通 for (size_t j = 0; j < n; j++) { if(board[0][j]=='O') uf->Union(n*0+j,dummy); if(board[m-1][j]=='O') uf->Union(n*(m-1)+j,dummy); } int dx[]={1,-1,0,0}; int dy[]={0,0,1,-1}; for(int i = 1;i<m-1;++i) for(int j= 1;j<n-1;++j){ if(board[i][j]=='O') { for(int k = 0;k<4;++k)//这里不用考虑越界问题 { int x =i+dx[k]; int y =j+dy[k]; if (board[x][y] == 'O') uf->Union(i*n+j,x*n+y); } } } for(int i = 1;i<m-1;++i) for(int j= 1;j<n-1;++j) { if(!uf->connected(i*n+j,dummy))//和dummy连通的点一定和边界的点连通 { board[i][j]='X'; } } return; } }; // @lc code=end