並查集:721賬戶的合併;947移除最多的同行或同列石頭;959由斜槓劃分區域

給定一個列表 accounts,每個元素 accounts[i] 是一個字符串列表,其中第一個元素 accounts[i][0] 是 名稱 (name),其餘元素是 emails 表示該帳戶的郵箱地址。

現在,我們想合併這些帳戶。如果兩個帳戶都有一些共同的郵件地址,則兩個帳戶必定屬於同一個人。請注意,即使兩個帳戶具有相同的名稱,它們也可能屬於不同的人,因爲人們可能具有相同的名稱。一個人最初可以擁有任意數量的帳戶,但其所有帳戶都具有相同的名稱。

合併帳戶後,按以下格式返回帳戶:每個帳戶的第一個元素是名稱,其餘元素是按順序排列的郵箱地址。accounts 本身可以以任意順序返回。

例子 1:

Input: 
accounts = [["John", "[email protected]", "[email protected]"], ["John", "[email protected]"], ["John", "[email protected]", "[email protected]"], ["Mary", "[email protected]"]]
Output: [["John", '[email protected]', '[email protected]', '[email protected]'],  ["John", "[email protected]"], ["Mary", "[email protected]"]]
Explanation: 
  第一個和第三個 John 是同一個人,因爲他們有共同的電子郵件 "[email protected]"。 
  第二個 John 和 Mary 是不同的人,因爲他們的電子郵件地址沒有被其他帳戶使用。
  我們可以以任何順序返回這些列表,例如答案[['Mary','[email protected]'],['John','[email protected]'],
  ['John','[email protected]','[email protected]','[email protected]']]仍然會被接受。

 

注意:


    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();
    }
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章