移情别恋c++ ദ്ദി˶ー̀֊ー́ ) ——14.哈希(1)
unordered系列关联式容器
在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到== l o g 2 N log_2 N log2N==,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好 的查询是,进行很少的比较次数就能够将元素找到,因此在C++11中,STL又提供了4个 u n o r d e r m a p \color{red}{unordermap} unordermap系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是 其**底层结构不同**
1. unordered_map
unordermap文档
unordered_map
:
unordered_map
是一种关联容器,它存储的是键值对(key-value pairs),并且键是唯一的。它使用哈希表来快速查找键。
特点:
键值对存储:每个元素是一个std::pair<const Key, T>
,其中Key
是键,T
是对应的值。无序存储:元素在哈希表中是无序存储的,插入的顺序不保证元素的顺序。唯一键:同一个键只能存在一个,如果插入相同键,会覆盖原有键对应的值。时间复杂度:查找、插入、删除的平均时间复杂度是O(1),但在最坏情况下,复杂度可能退化为O(n),比如在哈希冲突严重的情况下。 常用操作:
插入:umap.insert({key, value})
或 umap[key] = value
查找:umap.find(key)
返回迭代器,如果找不到则返回umap.end()
删除:umap.erase(key)
或 umap.erase(迭代器)
访问元素:umap[key]
如果键不存在,会自动插入该键并赋值为T()
(默认构造值)大小:umap.size()
返回元素个数 适用场景:
unordered_map
适合需要频繁进行键值对查找、插入、删除的场景,特别是在不关心元素顺序的情况下。比如统计元素频次、缓存(cache)实现等。
2. unordered_map的接口说明
1.unordered_map的构造
函数声明 | 功能介绍 |
---|---|
unordered_map | 构造不同格式的unordered_map对象 |
2.unordered_map的容量
函数声明 | 功能介绍 |
---|---|
bool empty() const | 检测unordered_map是否为空 |
size_t size() const | 获取unordered_map的有效元素个数 |
3. unordered_map的迭代器
begin | 返回unordered_map第一个元素的迭代器 |
---|---|
end | 返回unordered_map最后一个元素下一个位置 的迭代器 |
cbegin | 返回unordered_map第一个元素的const迭代器 |
cend | 返回unordered_map最后一个元素下一个位置的const迭代器 |
4.unordered_map的元素访问!!!
函数声明 | 功能介绍 |
---|---|
operator[] | 返回与key对应的value,若没有key则插入一个,并返回value默认值 |
5.unordered_map的查询
iterator find(constK& key) | 返回key在哈希桶中的位置 |
---|---|
size_t count(constK& key) | 返回哈希桶中关键码为key的键值对的个数 |
注意:**key是不能重复**的,因此count函数的返回值最大为1
6.unordered_map的修改操作
insert | 向容器中插入键值对 |
---|---|
erase | 删除容器中的键值对 |
clear | 清空容器中有效元素个数 |
void swap(unordered map&) | 交换两个容器中的元素 |
3. unordered_set
:
unordered_set
也是一个哈希容器,但它只存储唯一的键(没有值),也就是说它只关心元素是否存在,而不关心元素的具体值。
特点:
元素唯一:集合中的每个元素是唯一的,不能包含重复元素。无序存储:元素以无序的方式存储,插入顺序不影响元素的排列顺序。哈希表实现:与unordered_map
一样,unordered_set
基于哈希表实现。时间复杂度:查找、插入、删除的平均时间复杂度是O(1),但在最坏情况下也可能退化为O(n)。 常用操作:
插入:uset.insert(element)
插入元素查找:uset.find(element)
查找元素,返回迭代器,如果找不到则返回uset.end()
删除:uset.erase(element)
或 uset.erase(迭代器)
判断是否存在:uset.count(element)
返回元素是否存在,存在返回1,不存在返回0大小:uset.size()
返回集合中元素的个数 适用场景:
unordered_set
适合需要频繁进行集合操作的场景,比如元素去重、快速查找某个元素是否存在等。
4.小习题
https://leetcode.cn/problems/n-repeated-element-in-size-2n-array/description/
class Solution {public: int repeatedNTimes(vector<int>& nums) { int length = nums.size()/2; unordered_map<int, int> it; for (auto& e : nums) { it[e]++; } for (auto e : it) { if (e.second == length) { return e.first; } } return 1;//这里只是象征性地写一个return 1,不然会报错 }};
https://leetcode.cn/problems/uncommon-words-from-two-sentences/description/
class Solution {public: vector<string> uncommonFromSentences(string s1, string s2) { vector<string> arr; vector<string> brr; vector<string> crr; string it; it.clear(); for(auto e:s1) { if(e==' '||e=='\0') { arr.push_back(it); it.clear(); } else it+=e; } arr.push_back(it); it.clear(); map<string,int> a1; for(auto e:arr) { a1[e]++; } for(auto e:s2) { if(e==' '||e=='\0') { brr.push_back(it); it.clear(); } else it+=e; } brr.push_back(it); it.clear(); map<string,int> a2; for(auto e:brr) { a2[e]++; } auto it1=a1.begin(); auto it2=a2.begin(); while(it1!=a1.end()&&it2!=a2.end()) { if(it1->first!=it2->first) { if(it1->first<it2->first) { if(it1->second==1) crr.push_back(it1->first); it1++; } else { if(it2->second==1) crr.push_back(it2->first); it2++; } } else { it1++; it2++; } }while(it1!=a1.end()){ if(it1->second==1) crr.push_back(it1->first); it1++;}while(it2!=a2.end()){ if(it2->second==1) crr.push_back(it2->first); it2++;}return crr; }};
5.底层结构
unordered系列的关联式容器之所以效率比较高,是因为其底层使用了哈希结构
。
哈希概念:
顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素 时,必须要经过关键码的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度,即 O( l o g 2 N log_2 N log2N),搜索的效率取决于搜索过程中元素的比较次数。 理想的搜索方法:可以不经过任何比较,一次直接从表中得到要搜索的元素。 如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立 一一**映射**的关系,那么在查找时通过该函数可以很快找到该元素。
当向该结构中:
插入元素 根据待插入元素的关键码,以此函数==计算出该元素的存储位置
==并按此位置进行存放搜索元素 对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置 取元素比较,若关键码相等,则搜索成功 储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立 一一**映射**的关系,那么在查找时通过该函数可以很快找到该元素。
3. 二者的对比:
特性 | unordered_map | unordered_set |
---|---|---|
存储内容 | 键值对(key-value pairs) | 唯一元素(unique elements) |
键是否唯一 | 是 | 是 |
值 | 有 | 无 |
时间复杂度 | 平均O(1)(最坏O(n)) | 平均O(1)(最坏O(n)) |
顺序保证 | 无序 | 无序 |
适用场景 | 键值对的快速查找、插入、删除 | 元素集合的快速查找、插入、删除 |
4. 小结:
如果需要存储键值对并希望能够通过键快速访问相应的值,unordered_map
是更好的选择。如果仅需要存储唯一的元素并希望进行集合操作(如查找、插入、删除),unordered_set
更为合适。 两者的核心思想都是通过哈希函数来定位元素,从而提供快速的访问和操作。