Union Find
可以使用「并查集」解决的问题,一般都可以使用「深度优先搜索」和「广度优先搜索」完成。但是「深度优先搜索」和「广度优先搜索」不仅回答了连接问题,还回答了路径问题,时间复杂度高。
重点:
路径压缩
每个操作的复杂度是O(1)
class DisjointSet{
private:
vector<int> fa;
int _find(int x){
if(fa[x]==x)return x;
else{
int t = fa[x];
fa[x] = _find(t);
return fa[x];
}
};
public:
DisjointSet(int size){
for(int i=0;i<size;i++)fa.push_back(i);
};
int _union(int a, int b){
int fa_a = _find(a);
int fa_b = _find(b);
fa[fa_a] = fa_b;
return fa_b;
};
bool same(int a,int b){
return _find(a) == _find(b);
};
bool root(int a){
return fa[a] == a;
}
};
399. 除法求值
在并查集的基础上,设计辅助的数组记录信息,灵活运用路经压缩。
class Dict{
private:
map<string, int> str2int;
vector<string> int2str;
public:
Dict(){}
int push(string t){
if(!exists(t)){
str2int.insert(pair<string,int>(t, int2str.size()));
int2str.push_back(t);
}
return str2int[t];
}
bool exists(string t){
return str2int.count(t) != 0;
}
int const operator[](string t){
if(exists(t))
return str2int[t];
else
return -1;
}
string const operator[](int t){
return int2str[t];
}
int size(){
return int2str.size();
}
};
class DisjointSet{
public:
vector<int> fa;
vector<double> rate;
pair<int,double> _find(int x){
if(fa[x]==x)return pair<int,double>(x,1.0);
else{
int t = fa[x];
double r = rate[x];
pair<int,double> tmp = _find(t);
fa[x] = tmp.first;
rate[x] = r*tmp.second;
return pair<int,double>(fa[x],rate[x]);
}
};
DisjointSet(int size){
for(int i=0;i<size;i++){
fa.push_back(i);
rate.push_back(1.0);
}
};
int _union(int a, int b, double r){
pair<int,double> fa_a = _find(a);
pair<int,double> fa_b = _find(b);
fa[fa_a.first] = fa_b.first;
rate[fa_a.first] = r * fa_b.second / fa_a.second;
return fa_b.first;
};
bool same(int a,int b){
return _find(a).first == _find(b).first;
};
bool root(int a){
return fa[a] == a;
}
};
class Solution {
public:
vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
vector<double> ans;
Dict dict;
DisjointSet ds(500);
for(int i=0;i<equations.size();i++){
string a = equations[i][0];
string b = equations[i][1];
double v = values[i];
int ia = dict.push(a);
int ib = dict.push(b);
ds._union(ia, ib, v);
}
// for(int i=0;i<dict.size();i++){
// cout << "#" << i << endl;
// cout << dict[i] << endl;
// cout << ds.fa[i] << endl;
// cout << ds.rate[i] << endl;
// }
for(int i=0;i<queries.size();i++){
int ia = dict[queries[i][0]];
int ib = dict[queries[i][1]];
double t_ans;
if(ia == -1 || ib == -1){
t_ans = -1.0;
}else{
if(ds.same(ia,ib)){
double r1 = ds._find(ia).second;
double r2 = ds._find(ib).second;
t_ans = r1/r2;
}else{
t_ans = -1.0;
}
}
// cout << "ans:" << t_ans << endl;
ans.push_back(t_ans);
}
return ans;
}
};
959. 由斜杠划分区域
这个题目比较一般,没啥特殊的,将每个块分为四个,然后合并对应的块,最后看并查集的个数。
class DisjointSet{
private:
vector<int> fa;
int _find(int x){
if(fa[x]==x)return x;
else{
int t = fa[x];
fa[x] = _find(t);
return fa[x];
}
};
public:
DisjointSet(int size){
for(int i=0;i<size;i++)fa.push_back(i);
};
int _union(int a, int b){
int fa_a = _find(a);
int fa_b = _find(b);
fa[fa_a] = fa_b;
return fa_b;
};
bool same(int a,int b){
return _find(a) == _find(b);
};
bool root(int a){
return fa[a] == a;
}
int blocks(){
int cnt = 0;
for(int i=0;i<fa.size();i++){
if(root(i))cnt++;
}
return cnt;
}
};
class Solution {
public:
int regionsBySlashes(vector<string>& grid) {
DisjointSet ds(grid.size()*grid.size()*4);
for(int i=0;i<grid.size();i++){
int cnt_i = grid.size()*4*i;
for(int j=0;j<grid[i].size();j++){
if(j!=grid[i].size()-1){
ds._union(cnt_i+j*4+2,cnt_i+(j+1)*4+0);
}
if(grid[i][j]=='\\'){
ds._union(cnt_i+j*4+1,cnt_i+j*4+2);
ds._union(cnt_i+j*4+0,cnt_i+j*4+3);
}else if(grid[i][j]=='/'){
ds._union(cnt_i+j*4+0,cnt_i+j*4+1);
ds._union(cnt_i+j*4+2,cnt_i+j*4+3);
}else{
ds._union(cnt_i+j*4+0,cnt_i+j*4+1);
ds._union(cnt_i+j*4+1,cnt_i+j*4+2);
ds._union(cnt_i+j*4+2,cnt_i+j*4+3);
}
if(i!=grid.size()-1){
ds._union(cnt_i+j*4+3,cnt_i+grid.size()*4+j*4+1);
}
}
}
return ds.blocks();
}
};
778. 水位上升的泳池中游泳
这个题目有三种解法
结合二分查找做BFS或DFS遍历 $n^2*logn$
选择不同的高度,然后做遍历,看首尾是否可相连。
并查集
从小到大将点和他的上下左右合并操作,判断起点和终点是否在并查集中。
图的最短路径算法
动态维护从起点到各个定点的最短路径,加上
class DisjointSet{
private:
vector<int> fa;
int _find(int x){
if(fa[x]==x)return x;
else{
int t = fa[x];
fa[x] = _find(t);
return fa[x];
}
};
public:
DisjointSet(int size){
for(int i=0;i<size;i++)fa.push_back(i);
};
int _union(int a, int b){
int fa_a = _find(a);
int fa_b = _find(b);
fa[fa_a] = fa_b;
return fa_b;
};
bool same(int a,int b){
return _find(a) == _find(b);
};
bool root(int a){
return fa[a] == a;
}
};
class Solution {
public:
int dires[4][2] = {{0,1},{0,-1},{-1,0},{1,0}};
bool vaild(int x,int y,int N){
return (x>=0)&&(y>=0)&&(x<N)&&(y<N);
}
void deal(int i,vector<vector<int>>& grid,DisjointSet& ds){
int N = grid.size();
int x=i/N;
int y=i%N;
for(int j=0;j<4;j++){
int nx = x + dires[j][0];
int ny = y + dires[j][1];
if(vaild(nx,ny,N)){
if(grid[nx][ny]<=grid[x][y]){
ds._union(i,nx*N+ny);
}
}
}
}
int swimInWater(vector<vector<int>>& grid) {
int N = grid.size();
DisjointSet ds(N*N);
map<int, vector<int> > m;
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
int r = i*N+j;
int v = grid[i][j];
if(m.count(v)==0){
m.insert(pair<int,vector<int> >(v,vector<int>()));
}
m[v].push_back(r);
}
}
for(auto iter=m.begin();iter!=m.end();iter++){
for(int j=0;j<iter->second.size();j++){
deal(iter->second[j],grid,ds);
}
if(ds.same(0,N*N-1)){
return iter->first;
}
}
return 0;
}
};
图算法
#define inf 0x7fffffff
class node{
public:
int x;
int y;
int v;
node(int x,int y,int v):x(x),y(y),v(v){}
bool operator<(const node& b) const {
return v > b.v;
}
};
class Solution {
public:
int dires[4][2] = {{0,-1},{0,1},{1,0},{-1,0}};
bool vaild(int x,int y,int N){
return (x>=0)&&(y>=0)&&(x<N)&&(y<N);
}
int swimInWater(vector<vector<int>>& grid) {
int N = grid.size();
vector<vector<bool>> vis(N,vector<bool>(N,false));
priority_queue<node> dist;
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
if(i==0&&j==0)dist.push(node(i,j,grid[i][j]));
else dist.push(node(i,j,inf));
}
}
while(true){
auto t = dist.top();
dist.pop();
if(t.x == N-1 && t.y == N-1){
return t.v;
}
vis[t.x][t.y]=true;
for(int i=0;i<4;i++){
int nx = t.x + dires[i][0];
int ny = t.y + dires[i][1];
if(vaild(nx,ny,N)&&!vis[nx][ny]){
dist.push(node(nx,ny,max(t.v,grid[nx][ny])));
}
}
}
return 0;
}
};
1202. 交换字符串中的元素
可以推导出:交换关系可以传递
需要注意:
需要建立并查表的倒排,否则会超时间
class DisjointSet{
private:
vector<int> fa;
public:
int _find(int x){
if(fa[x]==x)return x;
else{
int t = fa[x];
fa[x] = _find(t);
return fa[x];
}
};
DisjointSet(int size){
for(int i=0;i<size;i++)fa.push_back(i);
};
int _union(int a, int b){
int fa_a = _find(a);
int fa_b = _find(b);
fa[fa_a] = fa_b;
return fa_b;
};
bool same(int a,int b){
return _find(a) == _find(b);
};
bool root(int a){
return fa[a] == a;
}
};
class Solution {
public:
string smallestStringWithSwaps(string s, vector<vector<int>>& pairs) {
DisjointSet ds(s.length());
for(int i=0;i<pairs.size();i++){
ds._union(pairs[i][0],pairs[i][1]);
}
map<int, vector<int> > m;
for(int i=0;i<s.length();i++){
int r = ds._find(i);
if(m.count(r)==0){
m.insert(pair<int,vector<int> >(r,vector<int>()));
}
m[r].push_back(i);
}
for(auto iter=m.begin();iter!=m.end();iter++){
vector<int> nums(27,0);
int root = iter->first;
vector<int> rs = iter->second;
for(int j=0;j<rs.size();j++){
nums[s[rs[j]]-'a']++;
}
int t=0;
for(int j=0;j<rs.size();j++){
while(nums[t]==0)t++;
s[rs[j]] = 'a'+t;
nums[t]--;
}
}
return s;
}
};
947. 移除最多的同行或同列石头
首先需要明确的是:同行同列的都属于一个并查集,然后按照规则每个并查集只会留下一颗石头。
class DisjointSet{
private:
vector<int> fa;
int _find(int x){
if(fa[x]==x)return x;
else{
int t = fa[x];
fa[x] = _find(t);
return fa[x];
}
};
public:
DisjointSet(int size){
for(int i=0;i<size;i++)fa.push_back(i);
};
int _union(int a, int b){
int fa_a = _find(a);
int fa_b = _find(b);
fa[fa_a] = fa_b;
return fa_b;
};
bool same(int a,int b){
return _find(a) == _find(b);
};
bool root(int a){
return fa[a] == a;
}
int count(){
int cnt=0;
for(int i=0;i<fa.size();i++)
if(root(i))cnt++;
return cnt;
}
};
class Solution {
public:
int removeStones(vector<vector<int>>& stones) {
int cnt = 0;
DisjointSet ds(stones.size());
map<int,vector<int>> m0,m1;
map<pair<int,int>, int> pos2rank;
for(int i=0;i<stones.size();i++){
pos2rank.insert(pair<pair<int,int>, int>(pair<int,int>(stones[i][0],stones[i][1]),i));
if(m0.count(stones[i][0])==0){
m0.insert(pair<int,vector<int>>(stones[i][0],vector<int>()));
}
m0[stones[i][0]].push_back(stones[i][1]);
if(m1.count(stones[i][1])==0){
m1.insert(pair<int,vector<int>>(stones[i][1],vector<int>()));
}
m1[stones[i][1]].push_back(stones[i][0]);
}
for(auto iter=m0.begin();iter!=m0.end();iter++){
int x = iter->first;
vector<int>& ys = iter->second;
for(int j=0;j<ys.size()-1;j++){
ds._union(pos2rank[pair<int,int>(x,ys[j])],pos2rank[pair<int,int>(x,ys[j+1])]);
}
}
for(auto iter=m1.begin();iter!=m1.end();iter++){
int y = iter->first;
vector<int>& xs = iter->second;
for(int j=0;j<xs.size()-1;j++){
ds._union(pos2rank[pair<int,int>(xs[j],y)],pos2rank[pair<int,int>(xs[j+1],y)]);
}
}
return stones.size()-ds.count();
}
};
803. 打砖块
Last updated
Was this helpful?