【C++进阶】哈希(万字详解)—— 学习篇(上)
🎇C++学习历程:入门
- 博客主页:一起去看日落吗
- 持续分享博主的C++学习历程
- 博主的能力有限,出现错误希望大家不吝赐教
- 分享给大家一句我很喜欢的话: 也许你现在做的事情,暂时看不到成果,但不要忘记,树🌿成长之前也要扎根,也要在漫长的时光🌞中沉淀养分。静下来想一想,哪有这么多的天赋异禀,那些让你羡慕的优秀的人也都曾默默地翻山越岭🐾。
🍁 🍃 🍂 🌿
目录
- 🍁 1. unordered系列关联式容器
- 🍂 1.1 unordered_map
- 🍃 1.1.1 unordered_map的文档介绍
- 🍃 1.1.2 unordered_map的接口说明
- 🍃 1.1.3 unordered_set的文档介绍
- 🍃 1.1.4 unordered_map和unordered_set的使用
- 🍂 1.2 在线OJ
- 🍁 2. 底层结构
- 🍂 2.1 哈希概念
- 🍂 2.2 哈希冲突
- 🍂 2.3 哈希函数
- 🍂 2.4 哈希冲突解决
- 🍃 2.4.1 闭散列
- 🍃 2.4.2 闭散列的实现
- 🍃 2.4.3 开散列
- 🍃 2.4.4 开散列的实现
🍁 1. unordered系列关联式容器
在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到 l o g 2 N log_2 N log2N,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好的查询是,进行很少的比较次数就能够将元素找到,因此在C++11中,STL又提供了4个unordered系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同,本文中只对unordered_map和unordered_set进行介绍,
unordered_multimap和unordered_multiset学生可查看文档介绍。
map和set底层是红黑树实现的,map是KV模型,set是K模型,而unordered_map和unordered_set底层是哈希表实现的,unordered_set是K模型,unordered_map是KV模型
unordered_map和unordered_set的命名体现特点,在功能和map/set是一样的,区别在于,它遍历出来是无序的,另外,它们的迭代器是单向迭代器
(图片来源网络,侵删)🍂 1.1 unordered_map
🍃 1.1.1 unordered_map的文档介绍
unordered_map在线文档说明
- unordered_map是存储键值对的关联式容器,其允许通过keys快速的索引到与其对应的value。
- 在unordered_map中,键值通常用于惟一地标识元素,而映射值是一个对象,其内容与此键关联。键
映射值的类型可能不同。
- 在内部,unordered_map没有对按照任何特定的顺序排序, 为了能在常数范围内找到key所对应的value,unordered_map将相同哈希值的键值对放在相同的桶中。
- unordered_map容器通过key访问单个元素要比map快,但它通常在遍历元素子集的范围迭代方面效率较低。
- unordered_maps实现了直接访问操作符(operator[]),它允许使用key作为参数直接访问value。
- 它的迭代器至少是前向迭代器。
🍃 1.1.2 unordered_map的接口说明
- unordered_map的构造
函数声明 功能介绍 unordered_map 构造不同格式的unordered_map对象 - unordered_map的容量
函数声明 功能介绍 bool empty() const 检测unordered_map是否为空 size_t size() const 获取unordered_map的有效元素个数 - unordered_map的迭代器
函数声明 功能介绍 begin 返回unordered_map第一个元素的迭代器 end 返回unordered_map最后一个元素下一个位置的迭代器 cbegin 返回unordered_map第一个元素的const迭代器 cend 返回unordered_map最后一个元素下一个位置的const迭代器 - unordered_map的元素访问
函数声明 功能介绍 operator[] 返回与key对应的value,没有一个默认值 注意:该函数中实际调用哈希桶的插入操作,用参数key与V()构造一个默认值往底层哈希桶中插入,如果key不在哈希桶中,插入成功,返回V(),插入失败,说明key已经在哈希桶中,将key对应的value返回。
- unordered_map的查询
函数声明 功能介绍 iterator find(const K& key) 返回key在哈希桶中的位置 size_t count(const K& key) 返回哈希桶中关键码为key的键值对的个数 注意:unordered_map中key是不能重复的,因此count函数的返回值最大为1
- unordered_map的修改操作
函数声明 功能介绍 insert 向容器中插入键值对 erase 删除容器中的键值对 void clear() 清空容器中有效元素个数 void swap(unordered_map&) 交换两个容器中的元素 - unordered_map的桶操作
函数声明 功能介绍 size_t bucket_count()const 返回哈希桶中桶的总个数 size_t bucket_size(size_t n)const 返回n号桶中有效元素的总个数 size_t bucket(const K& key) 返回元素key所在的桶号 🍃 1.1.3 unordered_set的文档介绍
unordered_set的在线文档介绍
🍃 1.1.4 unordered_map和unordered_set的使用
- unordered_set
#include #include #include using namespace std; void test_unordered_set() { unordered_set s; s.insert(3); s.insert(4); s.insert(5); s.insert(3); s.insert(1); s.insert(2); s.insert(6); unordered_set::iterator it = s.begin(); while (it != s.end()) { cout test_unordered_set(); return 0; } unordered_map cout test_unordered_map(); return 0; } public: int repeatedNTimes(vector size_t N = nums.size() / 2; unordered_map if(e.second == N) return e.first; } return 0; } }; public: vector // 用unordered_set对nums1中的元素去重 unordered_set if (s2.find(e) != s2.end()) vRet.push_back(e); } return vRet; } }; public: vector if (nums1.size() nums2.size()) { return intersect(nums2, nums1); } unordered_map ++m[num]; } vector if (m.count(num)) { intersection.push_back(num); --m[num]; if (m[num] == 0) { m.erase(num); } } } return intersection; } }; public: bool containsDuplicate(vector unordered_set if (s.find(x) != s.end()) { return true; } s.insert(x); } return false; } }; public: vector unordered_map stringstream ss(s); string word; while (ss word) { ++freq[move(word)]; } }; insert(s1); insert(s2); vector if (occ == 1) { ans.push_back(word); } } return ans; } }; enum Status { EMPTY,//空 EXIST,//存在 DELETE//删除 }; template pair size_t operator()(const K&key) { return key; } }; //特化 template size_t operator()(const string& key) { //BKDR Hash思想 size_t hash = 0; for(size_t i = 0;i hash*=131; hash += key[i];//转成整形 } return hash; } }; template public: bool Erase(const K& key) { HashData //没有这个值 return false; } else { //伪删除 ret-_status = DELETE; _n--; return true; } } HashData if(_table.size() == 0) { //防止除0错误 return nullptr; } Hash hf; size_t index = hf(key) % _table.size(); size_t i = 0; size_t index = start + i; while(_tables[index]._status != EMPTY) { if(_tabled[index]._kv.first == key && _table[index]._status == EXIST) { return &_tabled[index]; } else { ++i; //index = start+i;//线性探测 index = start+i*i;//二次探测 index %= _tables.size(); } } return nullptr; } //插入 bool Insert(const pair if(Find(kv.first)) { return false; } if(_table.size() == 0 || (double)(_n / _table.size()) 0.7) { //扩容 //方法二: size_t newSize = _table.size()==0? 10 : _table.size()*2; HashTable if(e._status == EXIST) { newHT.Insert(e._kv);//将旧表的数据插入新表 } } _table.swap(newHT._tables);//将新表和旧表交换 } Hash hf; size_t start = hf(kv.first) % _tables.size(); //线性探测 size_t i = 0; size_t index = start + i; while(_table[index]._status == EXIST) { ++index; if(index == _tables.size()) { //当index到达最后的时候,让它回到起始 index = 0; } //插满的时候会死循环 } //走到这里要么是空要么是删除 _tables[index]._kv = kv; _tables[index]._status = EXIST; ++_n; return true; } private: vector template pair} }; size_t GetNextPrime(size_t prime) { const int PRIMECOUNT = 28; static const size_t primeList[PRIMECOUNT] = { 53ul, 97ul, 193ul, 389ul, 769ul, 1543ul, 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul, 1610612741ul, 3221225473ul, 4294967291ul }; size_t i = 0; for (; i _kv.first == key) { // 1、cur是头结点 // 2、非头节点 if (prev == nullptr) { _tables[index] = cur->_next; } else { prev->_next = cur->_next; } delete cur; --_n; return true; } else { prev = cur; cur = cur->_next; } } return false; } Node* Find(const K& key) { if (_tables.size() == 0) { return nullptr; } Hash hf; size_t index = hf(key) % _tables.size(); Node* cur = _tables[index]; while (cur) { if (cur->_kv.first == key) { return cur; } else { cur = cur->_next; } } return nullptr; } bool Insert(const pair& kv) { Hash hf; //当负载因子到1时,进行扩容 if (_n == _tables.size()) { //size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2; size_t newSize = GetNextPrime(_tables.size()); //HashTable newHT; vector newtables; newtables.resize(newSize, nullptr); for (size_t i = 0; i _next; size_t index = hf(cur->_kv.first) % newSize; cur->_next = newtables[index]; newtables[index] = cur; cur = next; } _tables[i] = nullptr; } newtables.swap(_tables); } size_t index = hf(kv.first) % _tables.size(); Node* cur = _tables[index]; while (cur) { if (cur->_kv.first == kv.first) { return false; } else { cur = cur->_next; } } Node* newnode = new Node(kv); newnode->_next = _tables[index]; _tables[index] = newnode; ++_n; return true; } private: vector _tables; size_t _n = 0; // 存储多少有效数据 };
- unordered_set
- unordered_map的桶操作
- unordered_map的修改操作
- unordered_map的查询
- unordered_map的元素访问
- unordered_map的迭代器
- unordered_map的容量
还没有评论,来说两句吧...