130. 被围绕的区域
难度中等422
给定一个二维的矩阵,包含 '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
解释:
被围绕的区间不会存在于边界上,换句话说,任何边界上的 'O'
都不会被填充为 'X'
。 任何不在边界上,或不与边界上的 'O'
相连的 'O'
最终都会被填充为 'X'
。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
思路解析:
思路1.
首先正常的用并查集构建分支,然后遍历边界为“O”的点,用HashSet存储此点的根节点,再次遍历全图,如果为“O”点的根节点不在Hashset中,那么将“O”转变为“X”。
思路2:优化
定义一个节点 把所有边界的“O”节点的根节点设为它,然后正常用并查集建立分支,最后遍历全图,只有“O”节点的根节点不是它,就转变为“X”。
时间复杂度:定义find的最大路径长度为l,则时间复杂度为O(n*m*l)。
空间复杂度:O(n*m)。
思路1AC代码:
class Solution {
Map<Integer,Integer>root=new HashMap<>();
public int n,m;
public boolean isOk(int x,int y,char[][] board)
{
if(x>=0&&x<n&&y>=0&&y<m&&board[x][y]=='O')
return true;
return false;
}
public int getIndex(int x,int y)
{
return x*m+y;
}
public int find(int x)
{
while(x!=root.get(x))
{
root.put(root.get(x),root.get(root.get(x)));
x=root.get(x);//将x赋值为父亲节点的父亲节点。 从而实现路径压缩。
}
return x;
}
public void union(int x,int y)
{
int rootX=find(x);
int rootY=find(y);
if(rootX!=rootY)
root.put(rootX,rootY);
return ;
}
public void solve(char[][] board) {
n=board.length;
if(n==0)
return ;
m=board[0].length;
for(int i=0;i<n;i++)//初始化
for(int j=0;j<m;j++)
if(isOk(i,j,board))
root.put(getIndex(i,j),getIndex(i,j));
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if(isOk(i,j,board))
{
if(isOk(i-1,j,board))//只更新左上两个方向就ok
union(getIndex(i,j),getIndex(i-1,j));
if(isOk(i,j-1,board))
union(getIndex(i,j),getIndex(i,j-1));
}
Set<Integer>set=new HashSet<Integer>();
for(int i=0; i<n;i++)//列边界
{
if(isOk(i,0,board))
set.add(find(getIndex(i,0)));
if(isOk(i,m-1,board))
set.add(find(getIndex(i,m-1)));
}
for(int j=0; j<m;j++)//行边界
{
if(isOk(0,j,board))
set.add(find(getIndex(0,j)));
if(isOk(n-1,j,board))
set.add(find(getIndex(n-1,j)));
}
for(int i=0;i<n;i++)//遍历
for(int j=0;j<m;j++)
if(isOk(i,j,board)&&!set.contains(find(getIndex(i,j))))
board[i][j]='X';
return ;
}
}
思路2AC代码:
class Solution {
Map<Integer,Integer>root=new HashMap<>();
public int n,m;
public int dummyRoot=-1;
public boolean isOk(int x,int y,char[][] board)
{
if(x>=0&&x<n&&y>=0&&y<m&&board[x][y]=='O')
return true;
return false;
}
public boolean isEdge(int x,int y)
{
return x==0||y==0||x==n-1||y==m-1? true:false;
}
public int getIndex(int x,int y)
{
return x*m+y;
}
public int find(int x)
{
while(x!=root.get(x))
x=root.get(x);
return x;
}
public void union(int x,int y)
{
int rootX=find(x);
int rootY=find(y);
if(rootX!=rootY)
root.put(Math.max(rootX,rootY),Math.min(rootX,rootY));//为了使额外点成为根节点。
return ;
}
public void solve(char[][] board) {
n=board.length;
if(n==0)
return ;
m=board[0].length;
root.put(dummyRoot,dummyRoot);//设置额外点
for(int i=0;i<n;i++)//初始化
for(int j=0;j<m;j++)
if(isOk(i,j,board))
{
if(isEdge(i,j))
root.put(getIndex(i,j),dummyRoot);//边界“O”点的根节点设置为额外点。
else
root.put(getIndex(i,j),getIndex(i,j));
}
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if(isOk(i,j,board))
{
if(isOk(i-1,j,board))
union(getIndex(i,j),getIndex(i-1,j));
if(isOk(i,j-1,board))
union(getIndex(i,j),getIndex(i,j-1));
}
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if(isOk(i,j,board)&&find(getIndex(i,j))!=dummyRoot)
board[i][j]='X';
return ;
}
}
再提一点:
一、并查集有两个点需要注意:
1.可以加路径压缩。
2.小树加到大树上。需要+HashMap size;
二、
就是set集合可以了解一下了,我找了一篇不错的博客。