2562. 找出数组的串联值
递归地把整数数组的头尾相接,两数拼接为一个数字,求所有拼接数字的和。
题目链接
简单题,考察一个string与int之间的相互转换。
int到string使用std::to_string()
,string到int使用std::atoi()
即可。
代码略。
705&706. 设计hash集合/hash映射
不使用编程语言底层实现的hash库,自己实现可以添加/查找/删除/构造函数初始化的hash集合与hash映射(映射即(key,value)二元组)。
题目链接1 & 题目链接2
同样是简单题,主要关注hash表的两种策略,以及代码实现中一些C++的现代特性。
为了实现哈希集合这一数据结构,有以下几个关键问题需要解决:
- 哈希函数:能够将集合中任意可能的元素映射到一个固定范围的整数值,并将该元素存储到整数值对应的地址上。
- 冲突处理:由于不同元素可能映射到相同的整数值,因此需要在整数值出现「冲突」时,需要进行冲突处理。总的来说,有以下几种策略解决冲突:
- 链地址法:为每个哈希值维护一个链表,并将具有相同哈希值的元素都放入这一链表当中。
- 开放地址法:当发现哈希值 h h h处产生冲突时,根据某种策略,从 h h h出发找到下一个不冲突的位置。
- 再哈希法:当发现哈希冲突后,使用另一个哈希函数产生一个新的地址。
- 扩容:当哈希表元素过多时,冲突的概率将越来越大,而在哈希表中查询一个元素的效率也会越来越低。因此,需要开辟一块更大的空间,来缓解哈希表中发生的冲突。
哈希函数选用最简单的,直接对质数取模。(hash模数的因子越少,空间越多,则hash效果越好)。
冲突处理使用链地址法,为每个hash值维护一个链表。
容器选择使用vector,比较好写。
细节先看代码。
class HashSet{
vector<list<int>> data;//每个位置对应一个链表list
//hashlist换成 vector<list<pair<int,int>>> data 即可。
static int base = 857;//随意质数即可
inline int hash(int n) {return n%base;}
public:
HashList():data(base){};
void add(int key){
int h = hash(key);//取得hash值
//最标准的写法
for(auto it = data[h].begin(); it != data[h].end(); ++it){
if ((*it) == key) {
return;
}
//hashlist
if(it->first == key) it->second = value;
}
data[h].emplace_back(data);//*1
}
void remove(int key){
int h = hash(key);
for(auto it = data[h].begin(); it != data[h].end(); ++it){
if ((*it) == key) {
data[h].erase(it);
return;
}
}
}
int find(int key){
int h = hash(key);
for(auto it = data[h].begin(); it != data[h].end(); ++it){
if ((*it) == key) {
return 1;
}
//hashlist:
if(it->first == key) return it->second;
}
return -1;
}
}
注意题中的emplace_back。
emplace_back
是 C++11 的一个新特性。emplace_back() 和 push_back()
的区别是:push_back()
在向 vector 尾部添加一个元素时,首先会创建一个临时对象,然后再将这个临时对象移动或拷贝到 vector 中(如果是拷贝的话,事后会自动销毁先前创建的这个临时元素);而 emplace_back()
在实现时,则是直接在 vector 尾部创建这个元素,省去了移动或者拷贝元素的过程。
原文链接
另外,枚举关键字时使用auto比使用类型指明更加方便;枚举时懂得使用range from(:)也能节省大量的时间【例如:for(auto&& p:data[h])
】。