并查集:721账户的合并;947移除最多的同行或同列石头;959由斜杠划分区域

给定一个列表 accounts,每个元素 accounts[i] 是一个字符串列表,其中第一个元素 accounts[i][0] 是 名称 (name),其余元素是 emails 表示该帐户的邮箱地址。

现在,我们想合并这些帐户。如果两个帐户都有一些共同的邮件地址,则两个帐户必定属于同一个人。请注意,即使两个帐户具有相同的名称,它们也可能属于不同的人,因为人们可能具有相同的名称。一个人最初可以拥有任意数量的帐户,但其所有帐户都具有相同的名称。

合并帐户后,按以下格式返回帐户:每个帐户的第一个元素是名称,其余元素是按顺序排列的邮箱地址。accounts 本身可以以任意顺序返回。

例子 1:

Input: 
accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], ["John", "johnnybravo@mail.com"], ["John", "johnsmith@mail.com", "john_newyork@mail.com"], ["Mary", "mary@mail.com"]]
Output: [["John", 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'],  ["John", "johnnybravo@mail.com"], ["Mary", "mary@mail.com"]]
Explanation: 
  第一个和第三个 John 是同一个人,因为他们有共同的电子邮件 "johnsmith@mail.com"。 
  第二个 John 和 Mary 是不同的人,因为他们的电子邮件地址没有被其他帐户使用。
  我们可以以任何顺序返回这些列表,例如答案[['Mary','mary@mail.com'],['John','johnnybravo@mail.com'],
  ['John','john00@mail.com','john_newyork@mail.com','johnsmith@mail.com']]仍然会被接受。

 

注意:


    accounts的长度将在[1,1000]的范围内。
    accounts[i]的长度将在[1,10]的范围内。
    accounts[i][j]的长度将在[1,30]的范围内。

class Solution {
    unordered_map<string,string>parent;
    unordered_map<string,int>rank;
    unordered_map<string,list<string>>traverseChild;//list merge合并排序
    unordered_map<string,int>mailToNameIndex;
public:
    vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) {
        int m=accounts.size();        
        for(int i=0;i<m;++i){
            mailToNameIndex[accounts[i][1]]=i;
            Find(accounts[i][1]);
            for(int j=2;j<accounts[i].size();++j){
                mailToNameIndex[accounts[i][j]]=i;//mai2name
                Union(accounts[i][1],accounts[i][j]);
            }
        }
        vector<vector<string>>res;
        for(auto &t:traverseChild){
            vector<string>temp;
            temp.push_back(accounts[mailToNameIndex[t.first]][0]);
            for(auto &l:t.second)
                temp.push_back(l);
            res.push_back(temp);
        }
        return res;
    }
    string Find(string x){
        if(parent.count(x))
            return x==parent[x]?x:(parent[x]=Find(parent[x]));
        else{
            rank[x]=1;
            traverseChild[x].push_back(x);
            return (parent[x]=x);
        }
    }
    void Union(string a,string b){
        string fa=Find(a),fb=Find(b);
        if(fa==fb)return;
        if(rank[fa]<rank[fb]){
            parent[fa]=fb;
            traverseChild[fb].merge(traverseChild[fa]);
            traverseChild.erase(fa);
        }
        else if(rank[fa]>rank[fb]){
            parent[fb]=fa;
            traverseChild[fa].merge(traverseChild[fb]);
            traverseChild.erase(fb);
        }
        else{
            parent[fb]=fa;
            traverseChild[fa].merge(traverseChild[fb]);
            traverseChild.erase(fb);
            ++rank[fa];
        }
    }
};

我们将石头放置在二维平面中的一些整数坐标点上。每个坐标点上最多只能有一块石头。

每次 move 操作都会移除一块所在行或者列上有其他石头存在的石头。

请你设计一个算法,计算最多能执行多少次 move 操作?

 

示例 1:

输入:stones = [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]]
输出:5


示例 2:

输入:stones = [[0,0],[0,2],[1,1],[2,0],[2,2]]
输出:3


示例 3:

输入:stones = [[0,0]]
输出:0


 

提示:


    1 <= stones.length <= 1000
    0 <= stones[i][j] < 10000

class UnionFind{
    unordered_map<int,int>parent,rank,size;
    int maxSize;
public:
    UnionFind(vector<vector<int>>& stones){
        for(int i=0;i<stones.size();++i){
            int index=stones[i][0]*10000+stones[i][1];
            parent[index]=index;
            rank[index]=1;
            size[index]=1;
        }
        maxSize=0;
    }
    int Find(int x){
        return x==parent[x]?x:parent[x]=Find(parent[x]);
    }
    void Union(int a,int b){
        int fa=Find(a),fb=Find(b);
        if(fa==fb)return;
        if(rank[fa]<rank[fb]){
            parent[fa]=fb;
            size[fb]+=size[fa];
            size[fa]=0;
        }
        else if(rank[fa]>rank[fb]){
            parent[fb]=fa;
            size[fa]+=size[fb];
            size[fb]=0;
        }
        else{
            parent[fb]=fa;
            size[fa]+=size[fb];
            size[fb]=0;
            ++rank[fa];
        }
    }
    int getMaxSize(){//每个集合的个数-1的和就是答案,也等于stones.size()-集合个数cnt
        for(auto &i:size)
            if(i.second)maxSize+=(i.second-1);
        return maxSize;
    }
};
class Solution {
public:
    int removeStones(vector<vector<int>>& stones) {
        unordered_map<int,int>row,col;//同行/同列合并
        UnionFind uf(stones);
        for(int i=0;i<stones.size();++i){
            if(row.count(stones[i][0])==0&&col.count(stones[i][1])==0){
                row[stones[i][0]]=stones[i][0]*10000+stones[i][1];
                col[stones[i][1]]=row[stones[i][0]];
                continue;
            }
            if(row.count(stones[i][0])){                
                uf.Union(row[stones[i][0]],stones[i][0]*10000+stones[i][1]);
                if(col.count(stones[i][1]))
                    uf.Union(col[stones[i][1]],stones[i][0]*10000+stones[i][1]);
                else
                    col[stones[i][1]]=row[stones[i][0]];
            }
            else{                
                uf.Union(col[stones[i][1]],stones[i][0]*10000+stones[i][1]);
                row[stones[i][0]]=col[stones[i][1]];
            }            
        }
        return uf.getMaxSize();
    }
};
算法
对于一个坐标为 (i, j) 的石子来说,需要把行 i 和列 j 合并,因为并查集是一维的,用 j+10000 来代替 j。在将所有石子的行和列都合并好之后,只需数一下并查集中有几个集合就可以得到答案了。

 

class UnionFind{
    unordered_map<int,int>parent,rank;
    int cnt,initSize;
public:
    UnionFind(vector<vector<int>>& stones){
        initSize=stones.size();
        for(int i=0;i<initSize;++i){   
            //行         
            int index=stones[i][0];
            parent[index]=index;
            rank[index]=1; 
            //列
            index=10000+stones[i][1];
            parent[index]=index;
            rank[index]=1;              
        }
        cnt=parent.size();        
    }
    int Find(int x){
        return x==parent[x]?x:parent[x]=Find(parent[x]);
    }
    void Union(int a,int b){
        int fa=Find(a),fb=Find(b);
        if(fa==fb)return;
        if(rank[fa]<rank[fb])
            parent[fa]=fb;            
        else if(rank[fa]>rank[fb])
            parent[fb]=fa;            
        else{
            parent[fb]=fa;         
            ++rank[fa];
        }
        --cnt;
    }
    int getAns(){
        return initSize-cnt;
    }
};
class Solution {
public:
    int removeStones(vector<vector<int>>& stones) {
        unordered_map<int,int>row,col;
        UnionFind uf(stones);
        for(int i=0;i<stones.size();++i)
            uf.Union(stones[i][0],stones[i][1]+10000);
        return uf.getAns();
    }
};

在由 1 x 1 方格组成的 N x N 网格 grid 中,每个 1 x 1 方块由 /、\ 或空格构成。这些字符会将方块划分为一些共边的区域。

(请注意,反斜杠字符是转义的,因此 \ 用 "\\" 表示。)。

返回区域的数目。

 

 


示例 1:

输入:
[
  " /",
  "/ "
]
输出:2
解释:2x2 网格如下:


示例 2:

输入:
[
  " /",
  "  "
]
输出:1
解释:2x2 网格如下:


示例 3:

输入:
[
  "\\/",
  "/\\"
]
输出:4
解释:(回想一下,因为 \ 字符是转义的,所以 "\\/" 表示 \/,而 "/\\" 表示 /\。)
2x2 网格如下:


示例 4:

输入:
[
  "/\\",
  "\\/"
]
输出:5
解释:(回想一下,因为 \ 字符是转义的,所以 "/\\" 表示 /\,而 "\\/" 表示 \/。)
2x2 网格如下:


示例 5:

输入:
[
  "//",
  "/ "
]
输出:3
解释:2x2 网格如下:

 

 

提示:


    1 <= grid.length == grid[0].length <= 30
    grid[i][j] 是 '/'、'\'、或 ' '。

class UnionFind{
    vector<int>parent,rank;//指向父节点,秩/高度
    int cnt;//集合内部环个数
public:    
    UnionFind(int n){
        cnt=1;
        int N=pow(n,2);
        parent.resize(N,0);
        rank.resize(N,1);
        rank[0]=2;
        for(int i=1;i<n-1;++i)
            for(int j=1;j<n-1;++j)
                parent[i*n+j]=i*n+j;                
    }
    int Find(int x){
        return x==parent[x]?x:(parent[x]=Find(parent[x]));
    }
    void Union(int a,int b){
        int fa=Find(a),fb=Find(b);
        if(fa==fb){
            ++cnt;
            return;
        }
        if(rank[fa]>rank[fb])
            parent[fb]=fa;            
        else if(rank[fa]<rank[fb])
            parent[fa]=fb;
        else{
            parent[fa]=fb;
            ++rank[fb];
        }
    }
    int getCnt(){
        return cnt;
    }
};
class Solution {//方法一,并查集:合并对象是网格点坐标,不是网格!!!
public:
    int regionsBySlashes(vector<string>& grid) {//易错点:n的大小问题
        int n=grid.size()+1;
        UnionFind uf(n);//二维点[0~n][0~n],四周边上的点是同一个集合
        for(int i=0;i<n-1;++i){
            for(int j=0;j<n-1;++j){
                if(grid[i][j]=='\\')// \对应两点[i][j] [i+1][j+1]
                    uf.Union(i*n+j,(i+1)*n+j+1);                
                else if(grid[i][j]=='/')// /对应两点[i][j+1] [i+1][j]
                    uf.Union(i*n+j+1,(i+1)*n+j);
            }
        }
        return uf.getCnt();
    }
};

class UnionFind{
    vector<int>parent,rank;//指向父节点,秩/高度
    int cnt;//集合个数
public:    
    UnionFind(vector<vector<int>>& Grid){
        int n=Grid.size();
        cnt=0;
        parent.resize(n*n,-1);
        rank.resize(n*n,1);
        for(int i=0;i<n;++i)   
            for(int j=0;j<n;++j)
                if(Grid[i][j]==1)         
                    parent[i*n+j]=i*n+j,++cnt;           
    }
    int Find(int x){
        return x==parent[x]?x:(parent[x]=Find(parent[x]));
    }
    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;            
        else if(rank[fa]<rank[fb])
            parent[fa]=fb;
        else{
            parent[fa]=fb;
            ++rank[fb];
        }
        --cnt;
    }
    int getCnt(){
        return cnt;
    }
};
class Solution {//方法二,并查集:并查对象是扩大三倍后的网格,不是网格线!!!(速度很慢)
public:
    int regionsBySlashes(vector<string>& grid) {
        int n=grid.size();
        int N=3*n;
        vector<vector<int>>Grid(N,vector<int>(N,1));//相连为1表示连通     
        for(int ii=0;ii<n;++ii)
            for(int jj=0;jj<n;++jj)
                if(grid[ii][jj]=='\\'){
                    int i=3*ii,j=3*jj;
                    Grid[i][j]=Grid[i+1][j+1]=Grid[i+2][j+2]=0;//为0表示分界线
                }                                                   
                else if(grid[ii][jj]=='/'){
                    int i=3*ii,j=3*jj;
                    Grid[i][j+2]=Grid[i+1][j+1]=Grid[i+2][j]=0;
                }                    
        UnionFind uf(Grid);
        for(int i=0;i<N;++i)
            for(int j=0;j<N;++j)
                if(Grid[i][j]==1){//上下左右和当前节点都为不为分界线则合并
                    if(i<N-1&&Grid[i+1][j]==1)uf.Union(i*N+j,(i+1)*N+j);
                    if(j<N-1&&Grid[i][j+1]==1)uf.Union(i*N+j,i*N+j+1);
                    if(i>0&&Grid[i-1][j]==1)uf.Union(i*N+j,(i-1)*N+j);
                    if(j>0&&Grid[i][j-1]==1)uf.Union(i*N+j,i*N+j-1);
                }
        return uf.getCnt();
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值