基础知识
并查集是一种用来管理元素分组情况的数据结构,用于处理一些不交集(Disjoint Sets)的合并及查询问题。有一个联合-查找算法(Union-find Algorithm)定义了两个用于此数据结构的操作:
- Find:确定元素属于哪一个子集。它可以被用来确定两个元素是否属于同一子集。
- Union:将两个子集合并成同一个集合。
为了更加精确的定义这些方法,需要定义如何表示集合。一种常用的策略是为每个集合选定一个固定的元素,称为代表,以表示整个集合。接着,Find(x) 返回 x 所属集合的代表,而 Union 使用两个集合的代表作为参数。
基本结构
面试题 1
题目:
地铁迷在某个城市组织了地铁打卡活动。活动要求前往该城市中的所有地铁站进行打卡。打卡可以在站外或者站内进行。地铁的计价规则如下:只要不出站,就不计费;出站时,只计算进站和出站站点的距离。如在同一个站点进出站,按照最低票价 a 元计算。假设地铁票不会超时。大部分站点都是通过地铁线连通的,而且地铁站的连通是双向的(若 A,B 连通,则 B,A连通),且具有传递性的(若 A,B 连通,且 B,C 连通,则 A,C连通)。但并不是所有的地铁站都相互连通,在不能通过坐地铁达到的两个地点间,交通的花费则提高到 b 元。地铁迷从酒店起点出发,再回到酒店。假设从酒店到达任意地铁站的交通花费为 b 元。请计算地铁迷完成打卡最小交通花费。
输入描述:
每组输入包括m + 1行。
第1行包含4个整数n,m,a,b,其中n表示地铁站点数,m表示连通的地铁站点对数,a代表地铁最低票价,b代表非地铁方式票价,其中0 < a < b。
第2到m + 1行,每行2个整数Hi,Ti代表站点Hi和Ti站点是连通的(0 <= i< m)。
输出描述:
输出只有一行,包含一个整数,表示打卡的最小交通花费。
代码:
#include<iostream>
#include<vector>
using namespace std;
vector<int> f;
int find(int x) { //查找父类操作
return x == f[x] ? x : f[x] = find(f[x]);
}
void union1(int u,int v) { //合并操作
f[find(v)] = find(u);
}
int main() {
int n,m,a,b;
cin >> n >> m >> a >> b;
vector<int> row(2);
for(int i = 0;i < n;i++)
f.push_back(i);
for(int i = 0;i < m;i++) {
cin >> row[0] >> row[1];
union1(row[0],row[1]);
}
int num = 0;
for(int i = 0;i < n;i++)
if(f[i] == i)
num++;
int res = 2 * b + num * a + (num - 1) * b;
cout << res << endl;
return 0;
}
面试题 _婴儿名字
每年,政府都会公布一万个最常见的婴儿名字和它们出现的频率,也就是同名婴儿的数量。有些名字有多种拼法,例如,John 和 Jon 本质上是相同的名字,但被当成了两个名字公布出来。给定两个列表,一个是名字及对应的频率,另一个是本质相同的名字对。设计一个算法打印出每个真实名字的实际频率。注意,如果 John 和 Jon 是相同的,并且 Jon 和 Johnny 相同,则 John 与 Johnny 也相同,即它们有传递和对称性。
注:在结果列表中,选择字典序最小的名字作为真实名字。
代码:
//并查集
class Solution {
public:
vector<int>fa;
map<string,int>m1;//以str代表序列
map<int,int>m2;//以str表示个数
map<int,string>m3;//以序列寻找idx
int getfa(int x){
if(x!=fa[x]){ //x的父亲不是它自身
fa[x]=getfa(fa[x]); //找到x父亲的父亲,并赋值给x的父亲
}
return fa[x]; //返回x的父亲
}
void unionXY(string s1,string s2){
int x=getfa(m1[s1]); //获得s1的父亲结点 索引
int y=getfa(m1[s2]);
if(m3[x]>m3[y]){ //比较两个父亲,哪个更小
fa[x]=y;
}
else{
fa[y]=x;
}
}
pair<string,int>process(string str){
int pos1,pos2;
string name1,name2;
pos1 = str.find('(');
pos2 = str.find(')');
name1 = str.substr(0, pos1);
name2 = str.substr(pos1 + 1, pos2 - pos1 - 1);
return {name1,atoi(name2.c_str())};
}
pair<string,string>processSyn(string str){
string first,second;
int pos1 = str.find(',');
first = str.substr(1, pos1 - 1);
second = str.substr(pos1 + 1, str.size() - pos1 - 2);
return {first,second};
}
vector<string> trulyMostPopular(vector<string>& names, vector<string>& synonyms) {
int n=names.size();
int idx=0;
for(auto str:names){
pair<string,int>t=process(str); //拆分 名字 频率
m1[t.first]=idx; //map<名字,索引> m1
m2[idx]=t.second; //map<索引,频率> m2
m3[idx]=t.first; //map<索引,名字>
idx++;
}
fa.resize(n); //父结点的索引
for(int i=0;i<n;i++){
fa[i]=i;
}
for(auto str:synonyms){
pair<string,string>p=processSyn(str); //将相同的名字进行合并
unionXY(p.first,p.second);
}
vector<string>res;
map<int,int>nums;//表示个数
for(int i=0;i<n;i++){ //找到i的父亲,并更新其中的值
nums[getfa(i)]+=m2[i];
}
for(auto p:nums){
string tmp=m3[p.first];
tmp+='(';
tmp+=to_string(p.second);
tmp+=')';
res.push_back(tmp);
}
return res;
}
};