給定一個列表 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();
}
};