【C++】Unordered_map Unordered_set

        在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,例如map、set等。它们在搜索数据时效率可达到O(logN),但最糟糕的情况下搜索需要比较红黑树的高度次,若此时树中的节点非常之多,那么搜索效率就非常不理想。

        最理想的搜索是,进行较少的比较次数就能够将元素找到。于是,在C++11中,STL又提供了4个unordered系列的关联式容器,即unordered_map 、unordered_set、unordered_multimap和unordered_multiset。这四个容器与红黑树结构的关联式容器的使用方式基本类似,但不同的是,它们的底层结构为哈希桶。

        本篇博客unordered_map和unordered_set的特性和常见用法,并搭配对STL源码的模拟实现,旨在更好地帮助读者理解容器的功能。

目录

一、无序的关联式容器

1.Unordered_map 

2.Unordered_set

二、同一种底层数据结构支持两种不同的容器

1.底层哈希桶的实现

2.上层的初步封装

· unordered_map

· unordered_set

3.普通迭代器

· 基本结构

 · 相关功能

· 上层的进一步封装

4.const迭代器

· 基本结构

 · 上层的进一步封装

5.insert() 的返回值与operator[ ]

6.键值不匹配的解决方案

7.模拟实现的完整代码

· HashTable.h

· unordered_map.h

· unordered_set.h


一、无序的关联式容器

1.Unordered_map 

        Unordered_map(无序映像)是一种存储<key,value>键值对的关联式容器,使用时需包含该容器的头文件“#include<unordered_map>”,底层为哈希桶,支持通过key快速地索引到与之对应的value(为了能在常数范围内找到key所对应的value,它将具有相同哈希地址的键值对放在相同的桶中)。

        对于unordered_map中的元素,键值key通常用于惟一地标识它,实值value则表示它本身的值。键值key和实值value是一一对应的,它们的类型可以不同。它们在容器内没有对按照任何特定的顺序来排序,所以unordered_map相当于是map的”不排序但去重“版本。

        Unordered_map通过key访问单个元素要比map快(这是因为其底层是哈希桶),但在遍历迭代方面效率通常较低。它支持下标访问符[ ],允许使用key作为参数直接访问value。

        Unordered_map的迭代器一般为正向迭代器。

#include<unordered_map>void test_unordered_map()
{unordered_map<string, string> up;               //调用默认构造定义一个空的容器unordered_map<string, string> up2(up);//调用拷贝构造创建并初始化一个容器//emplace()构造并插入元素up.emplace("1", "apple");up.emplace("2", "banana");up.emplace("3", "orange");up.emplace("4", "pear");up.emplace("5", "watermelon");//迭代器遍历for ( auto it = up.begin(); it != up.end(); ++it ){cout << " " << it->first << ":" << it->second << endl;}//insert()插入元素up2.insert ( {{"sugar",0.8},{"salt",0.1}} );       //默认构造插入pair<string,double> myshopping ("baking powder",0.3);up2.insert (myshopping);                           //拷贝构造插入                       up2.insert (make_pair<string,double>("eggs",6.0)); //移动构造插入up2.insert (up.begin(), up.end());                 //迭代器范围插入//范围for遍历容器for (auto& kv : up2){cout << kv.first << ":" << kv.second << endl;}//通过[]也可以插入元素unordered_map<string, string> dict;dict["sort"] = "排序";dict["insert"] = "插入";dict["string"] = "字符串";dict["left"];for (auto& kv : dict){cout << kv.first << ":" << kv.second << endl;}//查找auto it = dict.find(sort); if ( it == dict.end() ) {cout << "not found" << endl;}else {cout << it->first << " is " << it->second << endl;}string s="insert";if (dict.count(s)>0){cout << "dict has " << s << endl;}//删除up.erase ( up.begin() );             //迭代器指定删除up.erase ("2");                      //key指定删除up.erase ( up.find("3"), up.end() ); //迭代器范围删除//验空+查大小if( up.empty() ){cout << up.size() << endl;}
}

2.Unordered_set

        Unordered_set(无序集合)是一种只存储键值key的关联式容器,使用时需包含该容器的头文件“#include<unordered_set>”,底层为哈希桶,支持对键值key的快速检索。

        对于unordered_set中的元素,它们的实值同时也是唯一标识它的键值。键值key在设置上是不可变的,因此,容器中的元素不可修改,只可插入和删除。容器中的元素没有对按照任何特定的顺序来排序,所以unordered_set相当于是set的”不排序但去重“版本。

        Unordered_set访问单个元素要比set快(这是因为其底层是哈希桶),但在遍历迭代方面效率通常较低。

        Unordered_set的迭代器一般为正向迭代器。

void test_unordered_set()
{//unordered_set仅能去重unordered_set<int> us;     //调用默认构造定义一个空的容器unordered_set<int> us2(us);//调用拷贝构造创建并初始化一个容器//插入us.insert(3);us.insert(1);us.insert(3);us.insert(4);us.insert(5);//迭代器遍历unordered_set<int>::iterator it = us.begin(); while (it != us.end()){cout << *it << "";//3 1 4 5 ++it;}cout << endl;//查找for (auto& x: us) {if (myset.count(x)>0) cout << "us has " << x << endl;else cout << "us has no " << x << endl;}unordered_set<int>::const_iterator got = us.find (6);if ( got == myset.end() ) cout << "not found in us";else cout << *got << " is in us";//删除us.erase ( us.begin() );                    us.erase (4);                        us.erase (us.find(1), us.end() ); }

二、同一种底层数据结构支持两种不同的容器

 (这一小节在【C++】map 和 set 中有逻辑相似的、更加详细的STL源码解析)

1.底层哈希桶的实现

(关于哈希桶的更多解析详见:【数据结构】哈希)

//HashTable.h#include<vector>template<class K>
struct DefaultHashFunc
{size_t operator()(const K& key){return (size_t)key;}
};
template<>
struct DefaultHashFunc<string>
{size_t operator()(const string& str){size_t hash = 0;for (auto ch : str){hash *= 131;hash += ch;}return hash;}
};namespace hash_bucket
{template<class T>struct HashNode{T _data;HashNode<T>* _next;HashNode(const T& data):_data(data),_next(nullptr){}};template<class K, class T, class KeyOfT, class HashFunc = DefaultHashFunc<K>>class HashTable{typedef HashNode<T> Node;public:HashTable(){_table.resize(10, nullptr);}~HashTable(){for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_table[i] = nullptr;}}bool Insert(const T& data){KeyOfT kot;if(Find(kot(data))){return false;}HashFunc hf;if (_n == _table.size()){size_t newSize = _table.size()*2;vector<Node*> newTable;newTable.resize(newSize, nullptr);for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->_next;size_t hashi = hf(kot(cur->_data)) % newSize;cur->_next = newTable[hashi];newTable[hashi] = cur;cur = next;}_table[i] = nullptr;}_table.swap(newTable);}size_t hashi = hf(kot(data)) % _table.size();Node* newnode = new Node(data);newnode->_next = _table[hashi];_table[hashi] = newnode;++_n;return true;}Node* Find(const K& key){HashFunc hf;KeyOfT kot;size_t hashi = hf(key) % _table.size();Node* cur = _table[hashi];while (cur){if (kot(cur->_data) == key){return cur;}cur = cur->_next;}return nullptr;}bool Erase(const K& key){HashFunc hf;KeyOfT kot;size_t hashi = hf(key) % _table.size();Node* prev = nullptr;Node* cur = _table[hashi];while (cur){if (kot(cur->_data) == key){if (prev == nullptr){_table[hashi] = cur->_next;}else{prev->_next = cur->_next;}--_n;delete cur;	return true;}prev = cur;cur = cur->_next;}return false;}private:vector<Node*> _table; // 挂满链表的指针数组size_t _n = 0;        // 有效数据个数};
}

2.上层的初步封装

· unordered_map

//unordered_map.h#include"HashTable.h"namespace CVE
{template<class K, class V>class unordered_map{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};private:hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> _ht;};
}// unordered_map -> hash_bucket::HashTable<K, pair<K, V>> _ht;
// unordered_set -> hash_bucket::HashTable<K, K> _ht;

· unordered_set

//unordered_set.h#include"HashTable.h"namespace CVE
{template<class K>class unordered_set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};private:hash_bucket::HashTable<K, K, SetKeyOfT> _ht;};
}// unordered_map -> hash_bucket::HashTable<K, pair<K, V>> _ht;
// unordered_set -> hash_bucket::HashTable<K, K> _ht;

3.普通迭代器

· 基本结构

//HashTable.hnamespace hash_bucket
{//哈希桶中链表节点template<class T>struct HashNode{T _data;HashNode<T>* _next;HashNode(const T& data):_data(data),_next(nullptr){}};//前置声明哈希桶//因为迭代器中要用哈希桶的指针作为成员template<class K, class T, class KeyOfT, class HashFunc>class HashTable;//普通迭代器template<class K, class T, class KeyOfT, class HashFunc>struct HTIterator{typedef HashNode<T> Node;typedef HTIterator<K, T, KeyOfT, HashFunc> Self;Node* _node;                             //哈希桶的当前节点HashTable<K, T, KeyOfT, HashFunc>* _pht; //哈希桶的指针//构造函数HTIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht):_node(node),_pht(pht){}//...//哈希桶(哈希表)template<class K, class T, class KeyOfT, class HashFunc = DefaultHashFunc<K>>class HashTable{//...public:typedef HTIterator<K, T, KeyOfT, HashFunc> iterator;//...};}

 · 相关功能

//HashTable.hnamespace hash_bucket
{//...template<class K, class T, class KeyOfT, class HashFunc>struct HTIterator{typedef HashNode<T> Node;typedef HTIterator<K, T, KeyOfT, HashFunc> Self;Node* _node;                             //哈希桶的当前节点HashTable<K, T, KeyOfT, HashFunc>* _pht; //哈希桶的指针HTIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht):_node(node),_pht(pht){}//*重载T& operator*(){return _node->_data; }//->重载T* operator->(){return &_node->_data;}//++重载Self& operator++(){//_node是某一个桶中的当前节点的指针// 当前桶中链表还没完,就遍历到下一个链表节点if (_node->_next){_node = _node->_next;}// 当前桶中链表走完了,就去找下一个挂了链表的桶(即不为空的桶)else{// 取当前桶的哈希地址KeyOfT kot;HashFunc hf;size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();// 去下一个相邻的哈希地址查找下一个不为空的桶++hashi;while (hashi < _pht->_table.size()){//找到了,将_node(当前节点的指针)置为找到的桶的指针,并返回if (_pht->_table[hashi]){_node = _pht->_table[hashi];return *this;}//没找到就再去下一个相邻的哈希地址找else{++hashi; }}_node = nullptr;}//遍历到桶中的下一个节点,或哈希表遍历完了,都返回当前节点(的指针)return *this;}//!=重载bool operator!=(const Self& s){return _node != s._node;}};template<class K, class T, class KeyOfT, class HashFunc = DefaultHashFunc<K>>class HashTable{typedef HashNode<T> Node;// 友元声明// 否则HTIterator的对象无法访问HashTable的私有成员template<class K, class T, class KeyOfT, class HashFunc>friend struct HTIterator;public:typedef HTIterator<K, T, KeyOfT, HashFunc> iterator;iterator begin(){// 找第一个桶for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];if (cur){return iterator(cur, this);}}return iterator(nullptr, this);}iterator end(){return iterator(nullptr, this);}//...}}

· 上层的进一步封装

  · unordered_map

//unordered_map.h#include"HashTable.h"namespace CVE
{template<class K, class V>class unordered_map{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT>::iterator iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}private:hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> _ht;};
}

 · unordered_set

//unordered_set.h#include"HashTable.h"namespace CVE
{template<class K>class unordered_set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::iterator iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}private:hash_bucket::HashTable<K, K, SetKeyOfT> _ht;};
}

4.const迭代器

· 基本结构

namespace hash_bucket
{template<class T>struct HashNode{T _data;HashNode<T>* _next;HashNode(const T& data):_data(data),_next(nullptr){}};template<class K, class T, class KeyOfT, class HashFunc>class HashTable;template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc>struct HTIterator{typedef HashNode<T> Node;typedef HTIterator<K, T, Ptr, Ref, KeyOfT, HashFunc> Self;typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> Iterator;Node* _node;const HashTable<K, T, KeyOfT, HashFunc>* _pht;HTIterator(Node* node, const HashTable<K, T, KeyOfT, HashFunc>* pht):_node(node), _pht(pht){}// 对于普通迭代器对象,这是拷贝构造// 对于const迭代器对象,这是构造,同时也支持普通迭代器去构造const迭代器HTIterator(const Iterator& it):_node(it._node), _pht(it._pht){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self& operator++(){if (_node->_next){_node = _node->_next;}else{KeyOfT kot;HashFunc hf;size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();++hashi;while (hashi < _pht->_table.size()){if (_pht->_table[hashi]){_node = _pht->_table[hashi];return *this;}else{++hashi;}}_node = nullptr;}return *this;}bool operator!=(const Self& s){return _node != s._node;}bool operator==(const Self& s){return _node == s._node;}};template<class K, class T, class KeyOfT, class HashFunc = DefaultHashFunc<K>>class HashTable{typedef HashNode<T> Node;// 友元声明template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc>friend struct HTIterator;public:typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> iterator;typedef HTIterator<K, T, const T*, const T&, KeyOfT, HashFunc> const_iterator;iterator begin(){for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];if (cur){return iterator(cur, this);}}return iterator(nullptr, this);}iterator end(){return iterator(nullptr, this);}const_iterator begin() const{for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];if (cur){return const_iterator(cur, this);}}return const_iterator(nullptr, this);}const_iterator end() const{return const_iterator(nullptr, this);}//...private:vector<Node*> _table; size_t _n = 0; };
}

 · 上层的进一步封装

 · unordered_map

#include"HashTable.h"namespace CVE
{template<class K, class V>class unordered_map{struct MapKeyOfT{const K& operator()(const pair<const K, V>& kv){return kv.first;}};public:typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::iterator iterator;typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}const_iterator begin() const{return _ht.begin();}const_iterator end() const{return _ht.end();}//...private:hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT> _ht;};
}

 · unordered_set

#pragma once#include"HashTable.h"namespace CVE
{template<class K>class unordered_set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator iterator;typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;const_iterator begin() const{return _ht.begin();}const_iterator end() const{return _ht.end();}//...private:hash_bucket::HashTable<K, K, SetKeyOfT> _ht;};
}

5.insert() 的返回值与operator[ ]

//HashTable.hnamespace hash_bucket
{//...template<class K, class T, class KeyOfT, class HashFunc = DefaultHashFunc<K>>class HashTable{typedef HashNode<T> Node;template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc>friend struct HTIterator;public:typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> iterator;typedef HTIterator<K, T, const T*, const T&, KeyOfT, HashFunc> const_iterator;//...pair<iterator, bool> Insert(const T& data){KeyOfT kot;iterator it = Find(kot(data));if(it != end()){return make_pair(it, false);}HashFunc hf;if (_n == _table.size()){//size_t newSize = _table.size() * 2;size_t newSize = GetNextPrime(_table.size());vector<Node*> newTable;newTable.resize(newSize, nullptr);for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->_next;size_t hashi = hf(kot(cur->_data)) % newSize;cur->_next = newTable[hashi];newTable[hashi] = cur;cur = next;}_table[i] = nullptr;}_table.swap(newTable);}size_t hashi = hf(kot(data)) % _table.size();Node* newnode = new Node(data);newnode->_next = _table[hashi];_table[hashi] = newnode;++_n;return make_pair(iterator(newnode, this), true);}iterator Find(const K& key){HashFunc hf;KeyOfT kot;size_t hashi = hf(key) % _table.size();Node* cur = _table[hashi];while (cur){if (kot(cur->_data) == key){return iterator(cur, this);}cur = cur->_next;}return end();}//...private:vector<Node*> _table; size_t _n = 0; };
}
//unordered_map.h#include"HashTable.h"namespace CVE
{template<class K, class V>class unordered_map{struct MapKeyOfT{const K& operator()(const pair<const K, V>& kv){return kv.first;}};public:typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::iterator iterator;typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}const_iterator begin() const{return _ht.begin();}const_iterator end() const{return _ht.end();}pair<iterator, bool> insert(const pair<K, V>& kv){return _ht.Insert(kv);}V& operator[](const K& key){pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));return ret.first->second;}private:hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT> _ht;};
}

6.键值不匹配的解决方案

//unordered_set.h#include"HashTable.h"namespace CVE
{template<class K>class unordered_set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator iterator;typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;const_iterator begin() const{return _ht.begin();}const_iterator end() const{return _ht.end();}pair<const_iterator, bool> insert(const K& key){pair<typename hash_bucket::HashTable<K, K, SetKeyOfT>::iterator, bool> ret = _ht.Insert(key);//先用一个<普通迭代器,布尔值>的对象接收插入方法的返回值return pair<const_iterator, bool>(ret.first, ret.second);//再将对象中的普通迭代器通过构造函数构造为const迭代器}private:hash_bucket::HashTable<K, K, SetKeyOfT> _ht;};
}

7.模拟实现的完整代码

· HashTable.h

#include<vector>template<class K>
struct DefaultHashFunc
{size_t operator()(const K& key){return (size_t)key;}
};
template<>
struct DefaultHashFunc<string>
{size_t operator()(const string& str){size_t hash = 0;for (auto ch : str){hash *= 131;hash += ch;}return hash;}
};namespace hash_bucket
{template<class T>struct HashNode{T _data;HashNode<T>* _next;HashNode(const T& data):_data(data),_next(nullptr){}};template<class K, class T, class KeyOfT, class HashFunc>class HashTable;template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc>struct HTIterator{typedef HashNode<T> Node;typedef HTIterator<K, T, Ptr, Ref, KeyOfT, HashFunc> Self;typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> Iterator;Node* _node;const HashTable<K, T, KeyOfT, HashFunc>* _pht;HTIterator(Node* node, const HashTable<K, T, KeyOfT, HashFunc>* pht):_node(node), _pht(pht){}HTIterator(const Iterator& it):_node(it._node), _pht(it._pht){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self& operator++(){if (_node->_next){_node = _node->_next;}else{KeyOfT kot;HashFunc hf;size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();++hashi;while (hashi < _pht->_table.size()){if (_pht->_table[hashi]){_node = _pht->_table[hashi];return *this;}else{++hashi;}}_node = nullptr;}return *this;}bool operator!=(const Self& s){return _node != s._node;}bool operator==(const Self& s){return _node == s._node;}};template<class K, class T, class KeyOfT, class HashFunc = DefaultHashFunc<K>>class HashTable{typedef HashNode<T> Node;template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc>friend struct HTIterator;public:typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> iterator;typedef HTIterator<K, T, const T*, const T&, KeyOfT, HashFunc> const_iterator;iterator begin(){for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];if (cur){return iterator(cur, this);}}return iterator(nullptr, this);}iterator end(){return iterator(nullptr, this);}const_iterator begin() const{for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];if (cur){return const_iterator(cur, this);}}return const_iterator(nullptr, this);}const_iterator end() const{return const_iterator(nullptr, this);}//每次快速取模的是一个类似两倍关系的素数,可以更好地支持除留余数法寻址和扩容//但这个方法并没有明确的理论依据//C++的STL库中用了这样的方法,而Java库中没有用size_t GetNextPrime(size_t prime){static const int __stl_num_primes = 28;static const unsigned long __stl_prime_list[__stl_num_primes] ={53,         97,         193,       389,       769,1543,       3079,       6151,      12289,     24593,49157,      98317,      196613,    393241,    786433,1572869,    3145739,    6291469,   12582917,  25165843,50331653,   100663319,  201326611, 402653189, 805306457,1610612741, 3221225473, 4294967291};size_t i = 0;for (; i < PRIMECOUNT; ++i){if (primeList[i] > prime)return primeList[i];}return primeList[i];}HashTable(){_table.resize(GetNextPrime(1), nullptr);}~HashTable(){for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_table[i] = nullptr;}}pair<iterator, bool> Insert(const T& data){KeyOfT kot;iterator it = Find(kot(data));if(it != end()){return make_pair(it, false);}HashFunc hf;if (_n == _table.size()){size_t newSize = GetNextPrime(_table.size());vector<Node*> newTable;newTable.resize(newSize, nullptr);for (size_t i = 0; i < _table.size(); i++){Node* cur = _table[i];while (cur){Node* next = cur->_next;size_t hashi = hf(kot(cur->_data)) % newSize;cur->_next = newTable[hashi];newTable[hashi] = cur;cur = next;}_table[i] = nullptr;}_table.swap(newTable);}size_t hashi = hf(kot(data)) % _table.size();Node* newnode = new Node(data);newnode->_next = _table[hashi];_table[hashi] = newnode;++_n;return make_pair(iterator(newnode, this), true);}iterator Find(const K& key){HashFunc hf;KeyOfT kot;size_t hashi = hf(key) % _table.size();Node* cur = _table[hashi];while (cur){if (kot(cur->_data) == key){return iterator(cur, this);}cur = cur->_next;}return end();}bool Erase(const K& key){HashFunc hf;KeyOfT kot;size_t hashi = hf(key) % _table.size();Node* prev = nullptr;Node* cur = _table[hashi];while (cur){if (kot(cur->_data) == key){if (prev == nullptr){_table[hashi] = cur->_next;}else{prev->_next = cur->_next;}--_n;delete cur;	return true;}prev = cur;cur = cur->_next;}return false;}private:vector<Node*> _table; size_t _n = 0; };
}

· unordered_map.h


#include"HashTable.h"namespace CVE
{template<class K, class V>class unordered_map{struct MapKeyOfT{const K& operator()(const pair<const K, V>& kv){return kv.first;}};public:typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::iterator iterator;typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}const_iterator begin() const{return _ht.begin();}const_iterator end() const{return _ht.end();}pair<iterator, bool> insert(const pair<K, V>& kv){return _ht.Insert(kv);}V& operator[](const K& key){pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));return ret.first->second;}private:hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT> _ht;};
}

· unordered_set.h

#include"HashTable.h"namespace CVE
{template<class K>class unordered_set{struct SetKeyOfT{const K& operator()(const K& key){return key;}};public:typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator iterator;typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;const_iterator begin() const{return _ht.begin();}const_iterator end() const{return _ht.end();}pair<const_iterator, bool> insert(const K& key){pair<typename hash_bucket::HashTable<K, K, SetKeyOfT>::iterator, bool> ret = _ht.Insert(key);return pair<const_iterator, bool>(ret.first, ret.second);}private:hash_bucket::HashTable<K, K, SetKeyOfT> _ht;};
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/728130.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python绘制趋势线

趋势线 趋势线是用来显示数据趋势或者预测未来发展方向的一种图形表示方法。在统计学和数据分析中&#xff0c;趋势线通常是通过拟合数据点来找到一条最符合数据整体趋势的直线、曲线或者其他形状。常见的趋势线拟合方法包括线性回归、多项式回归、指数平滑等。 趋势线在金融…

OpenHarmony教程指南—Ability的启动模式

介绍 本示例展示了在一个Stage模型中&#xff0c;实现standard、singleton、specified多种模式场景。 本实例参考开发指南 。 本实例需要使用aa工具 查看应用Ability 模式信息。 效果预览 使用说明 1、standard模式&#xff1a; 1&#xff09;进入首页&#xff0c;点击番茄…

贪心算法详解及机器人运动应用Demo

一、引言 贪心算法是一种在每一步选择中都采取在当前状态下最好或最优&#xff08;即最有利&#xff09;的选择&#xff0c;从而希望导致结果是全局最好或最优的算法。贪心算法在有最优子结构的问题中尤为有效。今天&#xff0c;我们将通过一个机器人运动的Demo来详细解析贪心算…

文心一言眼中的ChatGPT是什么样的

Q: 你好文心一言&#xff0c;请说一说你眼中的chatgpt A: 在我眼中&#xff0c;ChatGPT是一种非常先进和强大的自然语言处理模型&#xff0c;它展示了人工智能技术的显著进步。ChatGPT拥有出色的语言理解和生成能力&#xff0c;能够与用户进行流畅、自然的对话&#xff0c;并尝…

[c/c++] const

const 和 #define 的区别 ? const 和指针一块出现的时候&#xff0c;到底谁不能修改 &#xff1f; const 和 volatile 能同时修饰一个变量吗 ? const 在 c 中的作用 ? 1 const 和 #define 的区别 const 和 #define 的相同点&#xff1a; (1) 常数 const 和 #define 定…

lanqiao:合根植物

题目描述&#xff1a; 代码实现&#xff1a;

私域商业模式创新:消费增值引领企业业绩飙升

大家好&#xff0c;我是吴军&#xff0c;专注于私域商业模式的深度探索。今天&#xff0c;我要分享的是一个极具启发性的客户故事。这家企业&#xff0c;在短短一个月内&#xff0c;业绩飙升至上百万级别&#xff0c;用户活跃度同样瞩目&#xff0c;日均在线用户稳定在八万至十…

华为数通学习笔记(一):数据通信网络基础

华为数通学习笔记 前言&#xff1a;在学习大数据的过程中&#xff0c;我发现很多地方需要用到网络知识点&#xff0c;由于我哥考取了华为数通 HCIE 证书&#xff0c;目前正在一家大公司担任技术负责人&#xff0c;因此借此机会我要向他学习这方面的知识点&#xff0c;希望能够拓…

dbeaver更换下载驱动地址

DBeaver 是一个免费开源的数据库工具&#xff0c;提供对多种数据库系统的支持&#xff0c;包括 MySQL、PostgreSQL、Oracle、SQLite 等。它是一个通用的数据库管理工具&#xff0c;可以帮助用户连接、管理和查询各种类型的数据库。 下载地址 使用dbeaver连接数据库时需要先下…

Linux:kubernetes(k8s)探针LivenessProbe的使用(9)

他做的事情就是当我检测的一个东西他不在规定的时间内存在的话&#xff0c;我就让他重启&#xff0c;这个检测的目标可以是文件或者端口等 我这个是在上一章的基础之上继续操作&#xff0c;我会保留startupProbe探针让后看一下他俩的执行优先的一个效果 Linux&#xff1a;kuber…

洛谷P2233 公交车路线

本题题号特殊&#xff0c;相对简单。 题目描述 在长沙城新建的环城公路上一共有 88 个公交站&#xff0c;分别为 A、B、C、D、E、F、G、H。公共汽车只能够在相邻的两个公交站之间运行&#xff0c;因此你从某一个公交站到另外一个公交站往往要换几次车&#xff0c;例如从公交站…

【C++从0到王者】第五十站:B树

文章目录 一、内查找与外查找1.内查找2.外查找 二、B树概念三、B树的插入1.B树的插入分析2.B树插入总结3.插入代码实现4.B树满树和最空时候的对比5.B树的删除6.遍历B树7.B树的性能分析 一、内查找与外查找 1.内查找 像我们之前所用的在内存中的查找就是内查找 种类数据格式时…

C#,基于密度的噪声应用空间聚类算法(DBSCAN Algorithm)源代码

1 聚类算法 聚类分析或简单聚类基本上是一种无监督的学习方法&#xff0c;它将数据点划分为若干特定的批次或组&#xff0c;使得相同组中的数据点具有相似的属性&#xff0c;而不同组中的数据点在某种意义上具有不同的属性。它包括许多基于差分进化的不同方法。 E、 g.K-均值…

学习经验分享【NO.21】近期中文核心期刊目标检测论文理解

前言&#xff1a;最近比较忙&#xff0c;很久没有翻看知网论文了&#xff0c;看了下yolo改进相关的论文发现基于YOLOv5改进的核心期刊论文还是层出不穷&#xff0c;并没有因为已经是2024年了YOLOv9的出现而导致论文不好发&#xff0c;同时YOLOv8的论文也出了不少&#xff0c;所…

火柴排队(逆序对 + 离散化)

505. 火柴排队 原题链接 思路 如下是画图分析的一些过程 在这里贪心的思路是排序&#xff0c;然后两个数组都是从小到大那样对应的话最终的答案可达到最小 而我们只能交换相邻的火柴&#xff0c;故在这里先假设一个简化版本&#xff0c;即A有序&#xff0c;而只需要对B进行…

Java定时调度

在Java应用程序中&#xff0c;定时调度是一项重要的任务。它允许你安排代码执行的时间&#xff0c;以便在将来的某个时刻自动执行任务。Java提供了多种方式来实现定时调度&#xff0c;其中最常用的是Java的Timer和ScheduledExecutorService。 在本教程中&#xff0c;我们将学习…

990-39产品经理:Top 5 Most Common Incident Response Scenarios 五大最常见的事件响应场景

Top 5 Most Common Incident Response Scenarios 五大最常见的事件响应场景 Dealing with a cyber incident can be a daunting experience. Whether you’re targeted by phishing, malicious network scanning, or ransomware, it’s easy to feel overwhelmed. Even if you…

Frontend - Boostrap 消息弹窗

目录 一、下载 &#xff08;一&#xff09;中文官网 &#xff08;二&#xff09;bootstrap v3 依赖 jQuery 插件 二、解压并安装 &#xff08;一&#xff09;解压 1. 压缩包解压 2. 简化文件 &#xff08;二&#xff09;安装 三、配置 &#xff08;一&#xff09;bas…

Docker 配置阿里云镜像加速器

一、首先需要创建一个阿里云账号 二、登录阿里云账号 三、进入控制台 四、搜索容器镜像服务&#xff0c;并选择 五、选择镜像工具中的镜像加速 六 、配置镜像源 注意&#xff1a;有/etc/docker文件夹的直接从第二个命令开始

网络安全成全国两会热点话题,网络安全你知多少?

2024全国两会正召开的如火如荼&#xff0c;目前已诞生多个热点话题&#xff0c;比如教育、稳就业、促增收等等&#xff0c;其中网络安全也成全国两会热点话题之一。会上&#xff0c;多位全国政协委员、全国人大代表、行业专家、学者、企业大咖纷纷携网络安全相关提案、议案上会…