【C++】哈希的概念及STL中有关哈希容器的使用
目录
- 前言
- 一、unordered系列关联式容器
- 1.1 标准库中的unordered_set
- 1.1.1 unordered_set的介绍
- 1.1.2 unordered_set的常用接口说明
- 1.1.2.1 unordered_set对象的常见构造
- 1.1.2.1.1 [无参构造函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/)
- 1.1.2.1.2 [有参构造函数(使用迭代器进行初始化构造)](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/)
- 1.1.2.1.3 [拷贝构造函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/)
- 1.1.2.2 unordered_set iterator 的使用
- 1.1.2.2.1 [begin()函数](https://legacy.cplusplus.com/reference/unordered_set/unordered_set/begin/)
- 1.1.2.2.2 [end()函数](https://legacy.cplusplus.com/reference/unordered_set/unordered_set/end/)
- 1.1.2.3 unordered_set 对象的容量操作
- 1.1.2.3.1 [size()函数](https://legacy.cplusplus.com/reference/unordered_set/unordered_set/size/)
- 1.1.2.3.2 [empty()函数](https://legacy.cplusplus.com/reference/unordered_set/unordered_set/empty/)
- 1.1.2.4 unordered_set 对象的增删查改及访问
- 1.1.2.4.1 [insert()函数](https://legacy.cplusplus.com/reference/unordered_set/unordered_set/insert/)
- 1.1.2.4.2 [erase()函数](https://legacy.cplusplus.com/reference/unordered_set/unordered_set/erase/)
- 1.1.2.4.3 [swap()函数](https://legacy.cplusplus.com/reference/unordered_set/unordered_set/swap/)
- 1.1.2.4.4 [clear()函数](https://legacy.cplusplus.com/reference/unordered_set/unordered_set/clear/)
- 1.1.2.4.5 [find()函数](https://legacy.cplusplus.com/reference/unordered_set/unordered_set/find/)
- 1.1.2.4.6 [count()函数](https://legacy.cplusplus.com/reference/unordered_set/unordered_set/count/)
- 1.1.2.5 unordered_set的桶操作
- 1.1.2.5.1 [bucket_count()函数](https://legacy.cplusplus.com/reference/unordered_set/unordered_set/bucket_count/)
- 1.1.2.5.2 [bucket_size()函数](https://legacy.cplusplus.com/reference/unordered_set/unordered_set/bucket_size/)
- 1.1.2.5.3 [bucket()函数](https://legacy.cplusplus.com/reference/unordered_set/unordered_set/bucket/)
- 1.2 标准库中的unordered_map
- 1.2.1 unordered_map的介绍
- 1.2.2 unordered_map的常用接口说明
- 1.2.2.1 unordered_map对象的常见构造
- 1.2.2.1.1 [无参构造函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/unordered_map/)
- 1.2.2.1.2 [有参构造函数(使用迭代器进行初始化构造)](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/unordered_map/)
- 1.2.2.1.3 [拷贝构造函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/unordered_map/)
- 1.2.2.2 unordered_map iterator 的使用
- 1.1.2.2.1 [begin()函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/begin/)
- 1.2.2.2.2 [end()函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/end/)
- 1.2.2.3 unordered_set 对象的容量操作
- 1.2.2.3.1 [size()函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/size/)
- 1.2.2.3.2 [empty()函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/empty/)
- 1.2.2.4 unordered_map对象的增删查改及访问
- 1.2.2.4.1 [insert()函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/insert/)
- 1.2.2.4.2 [operator[]](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/operator%5B%5D/)
- 1.2.2.4.3 [erase()函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/erase/)
- 1.2.2.4.4 [swap()函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/swap/)
- 1.2.2.4.5 [clear()函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/clear/)
- 1.2.2.4.6 [find()函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/find/)
- 1.2.2.4.7 [count()函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/count/)
- 1.2.2.5 unordered_map的桶操作
- 1.2.2.5.1 [bucket_count()函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/bucket_count/)
- 1.2.2.5.2 [bucket_size()函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/bucket_size/)
- 1.2.2.5.3 [bucket()函数](https://legacy.cplusplus.com/reference/unordered_map/unordered_map/bucket/)
- 二、底层结构
- 2.1 哈希概念
- 2.2 哈希冲突
- 2.3 哈希函数
- 2.4 哈希冲突解决
- 2.4.1 闭散列
- 2.4.1.1 线性探测
- 2.4.1.2 二次探测
- 2.4.2 开散列
- 2.4.2.1.开散列概念
- 2.4.2.2 开散列实现及测试
- 2.4.2.3 开散列增容
- 2.4.2.4 开散列的思考
- 2.4.2.5 开散列与闭散列比较
- 结尾
前言
本篇文章将讲述关于哈希的概念、哈希函数、哈希冲突和冲突后的解决方案,具体有闭散列和开散列,并且文章中还有对闭散列中的线性探测、开散列的模拟实现和测试。文章中还会对 unordered_set 和 unordered_map 中经常使用的函数进行对概念和特性的讲解,还会介绍它们函数的使用方法。
由于不想文章篇幅太过长,有关于哈希表的实现、unordered_set 和 unordered_map封装实现、位图和布隆过滤器将会在后面的文章进行讲解。
一、unordered系列关联式容器
在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到 l o g 2 N log_2N log2N,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好的查询是,进行很少的比较次数就能够将元素找到,因此在C++11中,STL又提供了4个unordered系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同,本文中只对unordered_map和unordered_set进行介绍。
1.1 标准库中的unordered_set
1.1.1 unordered_set的介绍
unordered_set的文档介绍
- 在内部,unordered_set没有对按照任何特定的顺序排序。
- 在unordered_set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。unordered_set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
- 在unordered_set中,键值通常用于惟一地标识元素,而映射值是一个对象,其内容与此键关联。键和映射值的类型可能不同。
- 它的迭代器至少是前向迭代器。
unordered_set的模板参数列表介绍
Key:表示unordered_set中存储的元素类型,即键的类型,也是值的类型。
Hash:表示用于计算元素哈希值的哈希函数对象类型,默认为std::hash,用于确定元素在unordered_set内部存储位置的函数。
Pred:表示用于比较两个元素是否相等的函数对象类型,默认为std::equal_to,用于检查unordered_set中的元素是否相等。
Alloc:表示用于分配内存的分配器类型,默认为std::allocator,用于分配和释放unordered_set内存空间(目前不需要掌握)。
1.1.2 unordered_set的常用接口说明
1.1.2.1 unordered_set对象的常见构造
1.1.2.1.1 无参构造函数
unordered_set ( size_type n = /* see below */, const hasher& hf = hasher(), const key_equal& eql = key_equal());
#include #include using namespace std; int main() { unordered_set us; return 0; }
1.1.2.1.2 有参构造函数(使用迭代器进行初始化构造)
template unordered_set ( InputIterator first, InputIterator last, size_type n = /* see below */, const hasher& hf = hasher(), const key_equal& eql = key_equal());
int main() { string s("I Love You"); unordered_set us(s.begin(), s.end()); return 0; }
1.1.2.1.3 拷贝构造函数
unordered_set ( const unordered_set& ust );
int main() { string s("I Love You"); unordered_set us1(s.begin(), s.end()); unordered_set us2(us1); return 0; }
1.1.2.2 unordered_set iterator 的使用
由于 unordered_set 的迭代器是单向的,所以没有 rbegin() 和 rend() 函数 。
1.1.2.2.1 begin()函数
iterator begin() noexcept; const_iterator begin() const noexcept; 获取第一个数据位置的iterator/const_iterator
1.1.2.2.2 end()函数
iterator end() noexcept; const_iterator end() const noexcept; 获取最后一个数据的下一个位置的iterator/const_iterator
int main() { string s("I Love You"); unordered_set us(s.begin(), s.end()); unordered_set::iterator it = us.begin(); while (it != us.end()) { cout string s("I Love You"); unordered_set string s("I Love You"); unordered_set unordered_set ret = us.insert(i % 10); cout cout cout cout unordered_set 8,4,0,1,5,7,2,3 }; for (auto e : us) { cout cout cout cout unordered_set 1,3,5,7,9 }; unordered_set 0,2,4,6,8 }; cout cout cout cout cout unordered_set 1,3,5,7,9 }; cout unordered_set 1,3,5,7,9 }; unordered_set unordered_set 1,3,5,7,9 }; size_t count = us.count(9); return 0; } unordered_set us.insert(i + rand()); } cout unordered_map vector { "string","字符串" }, { "sort","排序" } }); unordered_map vector { "string","字符串" }, { "sort","排序" } }); unordered_map vector { "string","字符串" }, { "sort","排序" } }); unordered_map cout vector { "string","字符串" }, { "sort","排序" } }); unordered_map vector { "string","字符串" }, { "sort","排序" } }); unordered_map vector { "string","字符串" }, { "sort","排序" } }); unordered_map cout cout cout vector { "string","字符串" }, { "sort","排序" } }); unordered_map unordered_map { "string","字符串" }, { "sort","排序" } }); um.insert(make_pair("want", "想要")); um.insert(make_pair("love", "爱")); um.insert(make_pair("name", "名字")); for (auto pr : um) { cout cout cout cout unordered_map { "string","字符串" }, { "sort","排序" } }); unordered_map { "want", "想要" }, { "love", "爱" } }); cout cout cout cout cout unordered_map { "string","字符串" }, { "sort","排序" } }); cout unordered_map { "string","字符串" }, { "sort","排序" } }); unordered_map unordered_map { "string","字符串" }, { "sort","排序" } }); cout srand(time(0)); const size_t N = 10000; unordered_map int num = rand(); um.insert(make_pair(num, num)); } cout EMPTY, EXIST, DELETE}; size_t operator()(const K& key) { return (size_t)key; } }; // 特化 template size_t operator()(const string& s) { size_t sum = 0; for (size_t i = 0; i