模板
class UnionFind{
vector<int>parent,rank;
int cnt;
public:
UnionFind(vector<vector<char>>& board){
int m=board.size(),n=board[0].size();
cnt=m*n;
parent.resize(cnt);
rank.resize(cnt,1);
for(int i=0;i<cnt;++i)
parent[i]=i;
}
UnionFind(int n){
cnt=n;
parent.resize(n);
rank.resize(n,1);
for(int i=0;i<n;++i)
parent[i]=i;
}
int Find(int x){
return x==parent[x]?x:(parent[x]=Find(parent[x]));
}
bool isConnected(int a,int b){
int fa=Find(a),fb=Find(b);
return fa==fb;
}
void Union(int a,int b){
int fa=Find(a),fb=Find(b);
if(fa==fb)return;
if(rank[fa]>=rank[fb]){
parent[fb]=fa;
rank[fa]+=rank[fb];
}
else{
parent[fa]=fb;
rank[fb]+=rank[fa];
}
--cnt;
}
int getCnt(){
return cnt;
}
};
给定一个二维的矩阵,包含 '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'。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
class Solution {
public:
void solve(vector<vector<char>>& board) {
int m=board.size();
if(m<=2)return ;
int n=board[0].size();
if(n<=2)return ;
UnionFind uf(board);
int BounderO=0;
for(int i=0;i<m;++i){//首尾两列的O归为Bounder集合
if(board[i][0]=='O')uf.Union(i*n+0,BounderO);
if(board[i][n-1]=='O')uf.Union(i*n+n-1,BounderO);
}
for(int i=0;i<n;++i){//首尾两行的O归为Bounder集合
if(board[0][i]=='O')uf.Union(0*n+i,BounderO);
if(board[m-1][i]=='O')uf.Union((m-1)*n+i,BounderO);
}
for(int i=1;i<m-1;++i)
for(int j=1;j<n-1;++j)
if(board[i][j]=='O'){//跟上下左右的O集合合并
if(board[i-1][j]=='O')uf.Union(i*n+j,(i-1)*n+j);
if(board[i][j-1]=='O')uf.Union(i*n+j,(i)*n+j-1);
if(board[i+1][j]=='O')uf.Union(i*n+j,(i+1)*n+j);
if(board[i][j+1]=='O')uf.Union(i*n+j,(i)*n+j+1);
}
for(int i=1;i<m-1;++i)
for(int j=1;j<n-1;++j)
if(board[i][j]=='O'&&!uf.isConnected(BounderO,i*n+j))//不是BounderO结合的O改为X
board[i][j]='X';
}
};
给你一个数组 equations
,装着若干字符串表示的算式。每个算式 equations[i]
长度都是 4,而且只有这两种情况:a==b
或者 a!=b
,其中 a,b
可以是任意小写字母。你写一个算法,如果 equations
中所有算式都不会互相冲突,返回 true,否则返回 false。
比如说,输入 ["a==b","b!=c","c==a"]
,算法返回 false,因为这三个算式不可能同时正确。
再比如,输入 ["c==c","b==d","x!=z"]
,算法返回 true,因为这三个算式并不会造成逻辑冲突。
我们前文说过,动态连通性其实就是一种等价关系,具有「自反性」「传递性」和「对称性」,其实 ==
关系也是一种等价关系,具有这些性质。所以这个问题用 Union-Find 算法就很自然。
核心思想是,将 equations
中的算式根据 ==
和 !=
分成两部分,先处理 ==
算式,使得他们通过相等关系各自勾结成门派;然后处理 !=
算式,检查不等关系是否破坏了相等关系的连通性。
boolean equationsPossible(String[] equations) {
// 26 个英文字母
UF uf = new UF(26);
// 先让相等的字母形成连通分量
for (String eq : equations) {
if (eq.charAt(1) == '=') {
char x = eq.charAt(0);
char y = eq.charAt(3);
uf.union(x - 'a', y - 'a');
}
}
// 检查不等关系是否打破相等关系的连通性
for (String eq : equations) {
if (eq.charAt(1) == '!') {
char x = eq.charAt(0);
char y = eq.charAt(3);
// 如果相等关系成立,就是逻辑冲突
if (uf.connected(x - 'a', y - 'a'))
return false;
}
}
return true;
}
班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。
给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。
示例 1:
输入:
[[1,1,0],
[1,1,0],
[0,0,1]]
输出: 2
说明:已知学生0和学生1互为朋友,他们在一个朋友圈。
第2个学生自己在一个朋友圈。所以返回2。
示例 2:
输入:
[[1,1,0],
[1,1,1],
[0,1,1]]
输出: 1
说明:已知学生0和学生1互为朋友,学生1和学生2互为朋友,所以学生0和学生2也是朋友,所以他们三个在一个朋友圈,返回1。
注意:
N 在[1,200]的范围内。
对于所有学生,有M[i][i] = 1。
如果有M[i][j] = 1,则有M[j][i] = 1。
class Solution {
public:
int findCircleNum(vector<vector<int>>& M) {
int m=M.size();
if(m<=1)return m;
UnionFind uf(m);
for(int i=1;i<m;++i)
for(int j=0;j<i;++j)
if(M[i][j]==1)uf.Union(i,j);
return uf.getCnt();
}
};