个人主页:企鹅不叫的博客
专栏
- C语言初阶和进阶
- C项目
- Leetcode刷题
- 初阶数据结构与算法
- C++初阶和进阶
- 《深入理解计算机操作系统》
- 《高质量C/C++编程》
- Linux
⭐️ 博主码云gitee链接:代码仓库地址
⚡若有帮助可以【关注+点赞+收藏】,大家一起进步!
【初阶与进阶C++详解】第一篇:C++入门知识必备
【初阶与进阶C++详解】第二篇:C&&C++互相调用(创建静态库)并保护加密源文件
【初阶与进阶C++详解】第三篇:类和对象上(类和this指针)
【初阶与进阶C++详解】第四篇:类和对象中(类的六个默认成员函数)
【初阶与进阶C++详解】第五篇:类和对象下(构造+static+友元+内部类
【初阶与进阶C++详解】第六篇:C&C++内存管理(动态内存分布+内存管理+new&delete)
【初阶与进阶C++详解】第七篇:模板初阶(泛型编程+函数模板+类模板+模板特化+模板分离编译)
【初阶与进阶C++详解】第八篇:string类(标准库string类+string类模拟实现)
【初阶与进阶C++详解】第九篇:vector(vector接口介绍+vector模拟实现+vector迭代器区间构造/拷贝构造/赋值)
【初阶与进阶C++详解】第十篇:list(list接口介绍和使用+list模拟实现+反向迭代器和迭代器适配)
【初阶与进阶C++详解】第十一篇:stack+queue+priority_queue+deque(仿函数)
【初阶与进阶C++详解】第十二篇:模板进阶(函数模板特化+类模板特化+模板分离编译)
【初阶与进阶C++详解】第十三篇:继承(菱形继承+菱形虚拟继承+组合)
【初阶与进阶C++详解】第十四篇:多态(虚函数+重写(覆盖)+抽象类+单继承和多继承)
【初阶与进阶C++详解】第十五篇:二叉树搜索树(操作+实现+应用KVL+性能+习题)
【初阶与进阶C++详解】第十六篇:AVL树-平衡搜索二叉树(定义+插入+旋转+验证)
【初阶与进阶C++详解】第十七篇:红黑树(插入+验证+查找)
【初阶与进阶C++详解】第十八篇:map_set(map_set使用+multiset_multimap使用+模拟map_set)
【初阶与进阶C++详解】第十九篇:哈希(哈希函数+哈希冲突+哈希表+哈希桶)
- unordered_map是存储
键值对((KV模型)的关联式容器,其允许通过keys快速的索引到与其对应的value。 - 在unordered_map中,键值通常用于惟一地标识元素,而映射值是一个对象,其内容与此键关联。键和映射值的类型可能不同。
- 在内部,unordered_map没有对
按照任何特定的顺序排序(无序), 为了能在常数范围内找到key所对应的value,unordered_map将相同哈希值的键值对放在相同的桶中。 - unordered_map容器通过key访问单个元素要比map快,但它通常在遍历元素子集的范围迭代方面效率较低。
- unordered_map实现了直接访问操作符(operator[]),它允许使用key作为参数直接访问value。
- 它的迭代器是一个单向迭代器。
unordered_map ( const unordered_map& ump ) 拷贝构造
bool empty() const 检测unordered_map是否为空 size_t size() const 获取unordered_map的有效元素个数
begin 返回unordered_map第一个元素的迭代器 end 返回unordered_map最后一个元素下一个位置的迭代器 cbegin 返回unordered_map第一个元素的const迭代器 cend 返回unordered_map最后一个元素下一个位置的const迭代器
operator[] 返回与key对应的value,没有一个默认值 iterator find(const K& key) 返回key在哈希桶中的位置,查找的时间复杂度可以达到O(1) size_t count(const K& key) 返回哈希桶中关键码为key的键值对的个数,unordered_map中key是不能重复的,因此count函数的返回值最大为1
pair insert ( const value_type& val ) 向容器中插入键值对 iterator erase ( const_iterator position ) 删除容器中的键值对 void clear() 清空容器中有效元素个数 void swap(unordered_map&) 交换两个容器中的元素
测试代码:
void test_unordered_map() { unordered_map<int, int> um; map<int, int> m; int arr[] = { 4,2,3,1,6,8,9,3 }; for (auto e : arr) { um.insert(make_pair(e, e)); m.insert(make_pair(e, e)); } unordered_map<int, int>::iterator umit = um.begin(); map<int, int>::iterator mit = m.begin(); cout << "unordered_map:" << endl; while (umit != um.end()) { cout << umit->first << ":" << umit->second << endl; ++umit; } cout << "map:" << endl; while (mit != m.end()) { cout << mit->first << ":" << mit->second << endl; ++mit; } }
- unordered_set是以无特定顺序存储唯一元素的容器,并且允许根据它们的值快速检索单个元素,是一种K模型,不存储键值对,只存储Key。
- 在unordered_set中,元素的值同时是它的key,它唯一地标识它。键值是不可变的,因unordered_set中的元素不能在容器中修改一次 ,但是可以插入和删除它们。
- 在内部,unordered_set中的元素不是按任何特定顺序排序的,而是根据它们的哈希值组织成桶,以允许直接通过它们的值快速访问单个元素,时间复杂度可以达到O(1)。
- unordered_set容器比set容器更快地通过它们的key访问单个元素,尽管它们通常对于通过其元素的子集进行范围迭代的效率较低。
- 容器中的迭代器至少是单向迭代器。
unordered_set ( const unordered_set& ust ) 拷贝构造
bool empty() const 检测unordered_map是否为空 size_t size() const 获取unordered_map的有效元素个数
begin 返回unordered_set第一个元素的迭代器 end 返回unordered_set最后一个元素下一个位置的迭代器 cbegin 返回unordered_set第一个元素的const迭代器 cend 返回unordered_set最后一个元素下一个位置的const迭代器
operator[] 返回与key对应的value,没有一个默认值 iterator find(const K& key) 返回key在哈希桶中的位置,查找的时间复杂度可以达到O(1) size_t count(const K& key) 返回哈希桶中关键码为key的键值对的个数,unordered_map中key是不能重复的,因此count函数的返回值最大为1
pair insert ( const value_type& val ) 向容器中插入键值对 iterator erase ( const_iterator position ) 删除容器中的键值对 void clear() 清空容器中有效元素个数 void swap(unordered_set&) 交换两个容器中的元素
测试代码:
void test_unordered_set() { unordered_set<int> us; set<int> s; int arr[] = { 4,2,3,1,6,8,9,3 }; for (auto e : arr) { us.insert(e); s.insert(e); } unordered_set<int>::iterator usit = us.begin(); set<int>::iterator sit = s.begin(); cout << "unordered_set:" << endl; while (usit != us.end()) { cout << *usit << " "; ++usit; } cout << endl; cout << "set:" << endl; while (sit != s.end()) { cout << *sit << " "; ++sit; } cout << endl; }
map和set模拟实现
//模板参数第一个是key关键字,第二个参数不同容器V的类型不同,可以是K,也可以是pair
类型 //第三个参数因为T的类型不同,通过value取key的方式就不同,第四个参数就是一个仿函数,为了获取V中K的值然后取模 // unordered_map ->HashTable, MapKeyOfT> _ht; // unordered_set ->HashTable_ht; template<class K, class T, class KeyOfT, class HashFunc> class HashTable { typedef HashNode<T> Node; public: private: // 指针数组 vector<Node*> _tables; size_t _n = 0;// 记录表中的数据个数 };
map和set仿函数实现,我们用新增模板参数代替键值对来进行比较,两者调用同一棵树,用仿函数区分两者
template<class K, class HashFunc = DefaultHash<K>> class unordered_set { struct SetKeyOfT { const K& operator()(const K& key)//返回键值key { return key;//set只有一个键值 } }; }; ----------------------------------------------- template<class K, class V, class HashFunc = DefaultHash<K>> class unordered_map { struct MapKeyOfT { const K& operator()(const pair<K, V>& kv)//返回键值key { return kv.first;//map是键值对,需要提供kv的first进行比较 } }; };
下面是迭代器完整模板,哈希表的迭代器类型是单向迭代器,没有反向迭代器,即没有实现–运算符的重载
//声明哈希表 template<class K, class T, class KeyOfT, class HashFunc> class HashTable; //迭代器 //模板参数第一个是key关键字,第二个参数不同容器V的类型不同,可以是K,也可以是pair
类型 //第三个参数因为T的类型不同,通过value取key的方式就不同,第四个参数就是一个仿函数,为了获取V中K的值然后取模 template<class K, class T, class KeyOfT, class HashFunc> class __HTIterator { typedef HashNode<T> Node; typedef __HTIterator<K, T, KeyOfT, HashFunc> Self; public: Node* _node; HashTable<K, T, KeyOfT, HashFunc>* _pht; //初始化,节点指针和哈希表 __HTIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht) :_node(node) , _pht(pht) {} //前置++ Self& operator++() { if (_node->_next) { _node = _node->_next; } else { KeyOfT kot; HashFunc hf; size_t hashi = hf(kot(_node->_data)); //当前桶走完了 ++hashi; //遍历找下一个不为空的桶 for (; hashi < _pht->_tables.size(); ++hashi) { if (_pht->_tables[hashi]) { _node = _pht->_tables[hashi]; break; } } // 没有找到不为空的桶,用nullptr去做end标识 if (hashi == _pht->_tables.size()) { _node = nullptr; } } return *this; } T& operator*() { return _node->_data; } T* operator->() { return &_node->_data; } bool operator!=(const Self& s) const { return _node != s._node; } bool operator==(const Self& s) const { return _node == s._node; } };
将迭代器模板添加到哈希中,并且准备好普迭代器和const迭代器,和begin()和end()函数
// unordered_map ->HashTable
, MapKeyOfT> _ht; // unordered_set ->HashTable_ht; template<class K, class T, class KeyOfT, class HashFunc> class HashTable { //迭代器是哈希表的友元 //由于 ++ 重载函数在寻找下一个结点时,会访问哈希表成员变量 _table,而 _table 是哈希表的私有成员 template<class K, class T, class KeyOfT, class HashFunc> friend class __HTIterator; typedef HashNode<T> Node; public: typedef __HTIterator<K, T, KeyOfT, HashFunc> iterator; //第一个桶里的第一个节点的迭代器 iterator begin() { for (size_t i = 0; i < _tables.size(); ++i) { Node* cur = _tables[i]; if (cur) { //传送第一个节点数据,哈希表对象地址 return iterator(cur, this); } } return end(); } //最后一个数据的下一个位置 iterator end() { return iterator(nullptr, this); } private: // 指针数组 vector<Node*> _tables; size_t _n = 0; };
template<class K, class V, class HashFunc = DefaultHash<K>> class unordered_map { struct MapKeyOfT { const K& operator()(const pair<K, V>& kv) { return kv.first; } }; public: iterator begin() { return _ht.begin(); } iterator end() { return _ht.end(); } //配合后面·[]实现所以插入返回值使用pair
pair<iterator, bool> insert(const pair<K, V>& kv) { return _ht.Insert(kv); } iterator find(const K& key) { return _ht.Find(key); } bool erase(const K& key) { return _ht.Erase(key); } //得到Key返回对应的Value的引用 V& operator[](const K& key) { pair<iterator, bool> ret = insert(make_pair(key, V())); return ret.first->second; } private: Bucket::HashTable<K, pair<K, V>, MapKeyOfT, HashFunc> _ht; }; ------------------------------------------------------------ template<class K, class HashFunc = DefaultHash<K>> class unordered_set { struct SetKeyOfT { const K& operator()(const K& key) { return key; } }; public: //通过类域去取iterator,可能是内置类型或者静态成员变量,typename声明后面为一个类型名称,不是成员函数或者成员变量 typedef typename Bucket::HashTable<K, K, SetKeyOfT, HashFunc>::iterator iterator; iterator begin() { return _ht.begin(); } iterator end() { return _ht.end(); } pair<iterator, bool> insert(const K& key) { return _ht.Insert(key); } iterator find(const K& key) { return _ht.Find(key); } bool erase(const K& key) { return _ht.Erase(key); } private: Bucket::HashTable<K, K, SetKeyOfT, HashFunc> _ht; };
测试代码:
void test_unordered_map() { unordered_map<string, int> countMap; string strArr[] = { "香蕉","香蕉" ,"水蜜桃","西瓜","苹果","西瓜","香蕉" ,"苹果","西瓜","苹果","苹果","香蕉" ,"水蜜桃" }; for (auto& e : strArr) { countMap[e]++; } countMap["芒果"] = 10; for (auto& e : countMap) { cout << e.first << ":" << e.second << endl; } } ----------------------------------------------------- void test_unordered_set() { unordered_set<string> s; s.insert("sort"); s.insert("pass"); s.insert("cet6"); s.insert("pass"); s.insert("cet6"); s.erase("sort"); for (auto& e : s) { cout << e << endl; } }