C++ STL——hash/unordered_set/c++11关键字decltype

本文主要介绍了C++ STL中的hash类模板和unordered_set类模板,包括它们的定义、作用以及如何使用。通过示例代码展示了如何创建和操作unordered_set,并解释了hash函数的作用和find()、equal_range()等方法的功能。同时,文中还探讨了C++11关键字decltype在函数模板中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

摘自MSDN,以VS2012版为主


1、hash类模板

定义如下:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. template<class Ty>  
  2.     struct hash  
  3.         : public unary_function<Ty, size_t> {  
  4.     size_t operator()(Ty _Val) const;  
  5.     };  

并给出一个例子:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. // std_tr1__functional__hash.cpp   
  2. // compile with: /EHsc   
  3. #include <functional>   
  4. #include <iostream>   
  5. #include <unordered_set>   
  6.    
  7. int main()   
  8.     {   
  9.     std::unordered_set<int, std::hash<int> > c0;   
  10.     c0.insert(3);   
  11.     std::cout << *c0.find(3) << std::endl;   
  12.    
  13.     return (0);   
  14.     }   
  15.    
输出结果3


2.unordered_set类模板

定义:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. template<class Key,  
  2.     class Hash = std::hash<Key>,  
  3.     class Pred = std::equal_to<Key>,  
  4.     class Alloc = std::allocator<Key> >  
  5.     class unordered_set;  

Parameters

Parameter

Description

Key

The key type.

Hash

The hash function object type.

Pred

The equality comparison function object type.

Alloc

The allocator class.


结合第一个例子说明一下,
unordered_set是c++11引入的散列容器,在没有冲突的情况下,时间复杂度是常数时间。内部通过hash函数进行弱排序。

在说明unordered_set之前,先说明hash函数。


hash,百度百科中翻译为"散列"或者”哈希“,其通过一个hash函数,建立一个散列表,举个例子:


假设 (员工   工资);   zhao  3550;qian  4600;sun  4300;zhai 5210

设计一个hash函数,姓对应其首字母的ascii码,即zhao(Z)-90,qian(Q)-81,sun(S)-83,zhai(Z)-90

这就是所谓的  h(key)=value,h表示hash函数


这里第一个和第四个冲突,把第四个存在第一个旁边

开辟一个大小为200的数组H,H[90]=3550;H[81]=4600;H[83]=4300;H[91]=5210;

这种做法效率极其低下,但在没有冲突情况下是常数运行时间。


实际中,内存中是这么存储的,separate chaining:



LEN表示这个vec长度,可以看出一个vector,每个vec[i]暂且称为一个node,每个node后面一串叫做buckets,每个buckets是可变大小。



在回过头,讲unordered_set

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. std::unordered_set<int, std::hash<int> > c0;   

 

hash<int>是一个实例化后的函数类

文章开头,

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. size_t operator()(Ty _Val) const;  

实际上是对运算符()重载

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. *c0.find(3)  
相当于

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. *(c0.find(3))  


说明find()之前,需要了解equal_range;

equal_range:返回一个pair,即[X.first, X.second) ,其中X是迭代器,限制了key的范围

[cpp]  view plain  copy
  1. std::pair<iterator, iterator>  
  2.     equal_range(const Key& keyval);  
  3. std::pair<const_iterator, const_iterator>  
  4.     equal_range(const Key& keyval) const;  

[cpp]  view plain  copy
  1. // std_tr1__unordered_set__unordered_set_equal_range.cpp   
  2. // compile with: /EHsc   
  3. #include <unordered_set>   
  4. #include <iostream>   
  5.   
  6. typedef std::unordered_set<char> Myset;   
  7. int main()   
  8.     {   
  9.     Myset c1;   
  10.   
  11.     c1.insert('a');   
  12.     c1.insert('b');   
  13.     c1.insert('c');   
  14.   
  15. // display contents " [c] [b] [a]"   
  16.     for (Myset::const_iterator it = c1.begin();   
  17.         it != c1.end(); ++it)   
  18.         std::cout << " [" << *it << "]";   
  19.     std::cout << std::endl;   
  20.   
  21. // display results of failed search   
  22.     std::pair<Myset::iterator, Myset::iterator> pair1 =   
  23.         c1.equal_range('x');   
  24.     std::cout << "equal_range('x'):";   
  25.     for (; pair1.first != pair1.second; ++pair1.first)   
  26.         std::cout << " [" << *pair1.first << "]";   
  27.     std::cout << std::endl;   
  28.   
  29. // display results of successful search   
  30.     pair1 = c1.equal_range('b');   
  31.     std::cout << "equal_range('b'):";   
  32.     for (; pair1.first != pair1.second; ++pair1.first)   
  33.         std::cout << " [" << *pair1.first << "]";   
  34.     std::cout << std::endl;   
  35.   
  36.     return (0);   
  37.     }   

结果:

[a]  [b]   [c]
equal_range('x'):
equal_range('b'):    [b]


再看一个find的例子

[cpp]  view plain  copy
  1. const_iterator find(const Key& keyval) const;  

find返回 unordered_set::equal_range(keyval).first

[cpp]  view plain  copy
  1. // std_tr1__unordered_set__unordered_set_find.cpp   
  2. // compile with: /EHsc   
  3. #include <unordered_set>   
  4. #include <iostream>   
  5.   
  6. typedef std::unordered_set<char> Myset;   
  7. int main()   
  8. {   
  9.     Myset c1;   
  10.   
  11.     c1.insert('a');   
  12.     c1.insert('b');   
  13.     c1.insert('c');   
  14.   
  15.     // display contents " [c] [b] [a]"   
  16.     // 我用VS2012编译是" [a] [b] [c]"  
  17.     for (Myset::const_iterator it = c1.begin();   
  18.         it != c1.end(); ++it)   
  19.         std::cout << " [" << *it << "]";   
  20.     std::cout << std::endl;   
  21.   
  22.     // try to find and fail   
  23.     std::cout << "find('A') == "   
  24.         << std::boolalpha << (c1.find('A') != c1.end()) << std::endl;   
  25.   
  26.     // try to find and succeed   
  27.     Myset::iterator it = c1.find('b');   
  28.     std::cout << "find('b') == "   
  29.         << std::boolalpha << (it != c1.end())   
  30.         << ": [" << *it << "]" << std::endl;   
  31.   
  32.     return (0);   
  33. }   


结果:

[a]  [b]  [c]

find('A')==false

find('b')==true: [b]

要注意的是:

[cpp]  view plain  copy
  1. c1.end()  
不指向最后一个元素,而指向最后一个元素再+1,当find不到输入的元素时候,返回迭代器就指向这个end()



更复杂的代码分析:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #include <iostream>   
  2. #include <unordered_set>  
  3.   
  4.   
  5.   
  6.   
  7. using namespace std;  
  8.   
  9.   
  10. size_t hash_function_pair_int(pair<intint> p)  
  11. {  
  12. return hash<int>()(p.first - p.second);  
  13. }  
  14.   
  15.   
  16. typedef  unordered_set < pair<intint>, decltype(hash_function_pair_int)* > set_pair_int;  
  17.   
  18.   
  19. void main()  
  20. {  
  21. set_pair_int boundEdge(10,hash_function_pair_int);  
  22.   
  23.   
  24. pair<int,int> f0(4,3),f1(3,1),f2(3,2),f3(3,2);  
  25. boundEdge.insert(f0);  
  26. boundEdge.insert(f1);  
  27. boundEdge.insert(f2);  
  28. boundEdge.insert(f3);  
  29. }  

关键字decltype自动推断表达式类型;

hash<int>()是一个匿名对象,

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. hash<int>()(p.first - p.second)  

后面第二个()是操作符重载,输入参数是pair的first和second的差

<think>嗯,我现在需要理解C++中的setunordered_set和multiset之间的区别。首先,我回忆一下,这些都是容器,属于STL的一部分。但具体每个有什么特点呢? 我记得set是一个有序的集合,里面的元素是唯一的,按照某种顺序排列。而unordered_set可能和哈希表有关,元素是无序的。multiset应该允许重复的元素,对吗?但是具体它们之间的区别可能需要更详细的对比。 首先,数据结构方面。set通常是用红黑树实现的,这样元素可以自动排序,保持有序状态。而unordered_set应该是基于哈希表,所以元素存储的位置由哈希函数决定,没有特定的顺序。multisetset类似,但允许重复元素,所以它应该也是用红黑树,但节点可以存储多个相同值。 接下来是元素唯一性。setunordered_set中的元素都是唯一的,不能重复。而multiset允许重复,所以插入相同的元素不会失败。同样,unordered_multiset应该也有类似的功能,但基于哈希表,允许重复且无序。 关于性能,查找操作在set中是O(log n),因为红黑树是平衡二叉搜索树。而unordered_set的查找平均是O(1),最坏情况可能是O(n),比如哈希冲突很多的时候。插入和删除操作的时间复杂度应该类似,set是O(log n),unordered_set平均O(1)。multiset的插入删除也是O(log n),因为需要维护顺序。而unordered_multiset则平均O(1)。 迭代器的稳定性方面,set和multiset在插入或删除元素时,除了被操作的元素,其他元素的迭代器不会失效。而unordered_setunordered_multiset在rehash的时候,所有迭代器都可能失效,所以稳定性差一些。 内存占用的话,哈希表结构的容器可能需要更多内存,因为需要维护哈希桶,而红黑树的结构可能更紧凑一些。 应用场景的话,如果需要有序访问元素,或者频繁进行范围查询,应该用set或multiset。如果更关注快速查找、插入和删除,并且不需要顺序,那么unordered系列更好。multisetunordered_multiset适用于允许重复元素的场景。 还有,头文件方面,set和multiset在<set>头文件里,unordered系列在<unordered_set>里。C++标准方面,unordered系列是C++11引入的,而set和multiset则更早。 比如,当需要统计单词出现次数并且需要按字母顺序输出时,可以用multiset。而如果是高频的查找操作,比如缓存系统,用unordered_set会更高效。 可能的问题还有它们的内部实现细节,比如哈希函数的选择对unordered_set性能的影响。还有,当元素频繁插入删除时,两种结构的性能差异。此外,如何选择是否允许重复元素,以及是否需要有序遍历等因素。</think>### C++中`set`、`unordered_set`、`multiset`的区别对比 #### 1. **数据结构与元素特性** - **`set`** - 基于红黑树(平衡二叉搜索树)实现,元素**有序**且**唯一**。 - 插入/删除/查找时间复杂度:$O(\log n)$。 - 适用场景:需有序遍历或范围查询(如找最小/最大值)[^1][^3]。 - **`unordered_set`** - 基于哈希表实现,元素**无序**且**唯一**。 - 插入/删除/查找平均时间复杂度:$O(1)$(最坏情况$O(n)$,取决于哈希冲突)。 - 适用场景:高频查找且无需顺序,例如缓存机制[^2]。 - **`multiset`** - 类似`set`,但允许**重复元素**,基于红黑树实现,元素有序。 - 插入/删除/查找时间复杂度:$O(\log n)$。 - 适用场景:统计有序重复数据(如按分数排序的成绩表)[^3]。 - **`unordered_multiset`** - 类似`unordered_set`,但允许**重复元素**,基于哈希表实现,元素无序。 - 插入/删除/查找平均时间复杂度:$O(1)$[^3]。 #### 2. **功能特性对比** | 特性 | `set` | `unordered_set` | `multiset` | `unordered_multiset` | |--------------------|----------------|------------------|-----------------|----------------------| | 元素唯一性 | ✔️ | ✔️ | ❌(允许重复) | ❌(允许重复) | | 元素顺序 | 有序 | 无序 | 有序 | 无序 | | 底层结构 | 红黑树 | 哈希表 | 红黑树 | 哈希表 | | 头文件 | `<set>` | `<unordered_set>`| `<set>` | `<unordered_set>` | | 迭代器稳定性 | 稳定(插入/删除不影响其他迭代器) | 可能失效(哈希表扩容时) | 同`set` | 同`unordered_set` | #### 3. **性能分析** - **查找性能** - `unordered_set`通常优于`set`(哈希表平均$O(1)$ vs. 红黑树$O(\log n)$)。 - 但哈希表性能受负载因子和哈希函数影响,极端情况下可能退化为$O(n)$[^2]。 - **插入/删除性能** - `unordered_set`插入/删除平均更快,但需处理哈希冲突;`set`在有序插入时可能更高效(避免频繁调整哈希表)。 - **内存占用** - `unordered_set`因哈希表需预分配桶空间,内存占用通常高于`set`。 #### 4. **应用场景示例** - **`set`** - 需要有序且唯一的数据,如字典单词存储。 - **`unordered_set`** - 高频查询且无需顺序,如URL去重。 - **`multiset`** - 有序且允许重复,如按时间戳记录事件。 - **`unordered_multiset`** - 高频插入/删除且允许重复,如实时统计词频。 #### 5. **代码示例** ```cpp #include <set> #include <unordered_set> int main() { // set(有序唯一) std::set<int> s = {3, 1, 4, 1, 5}; // 实际存储 {1, 3, 4, 5} // unordered_set(无序唯一) std::unordered_set<int> us = {3, 1, 4, 1, 5}; // 存储顺序不确定 // multiset(有序可重复) std::multiset<int> ms = {3, 1, 4, 1, 5}; // 存储 {1, 1, 3, 4, 5} // unordered_multiset(无序可重复) std::unordered_multiset<int> ums = {3, 1, 4, 1, 5}; // 存储顺序不确定 return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值