map&&set
- unordered_map&&unordered_set介绍
- unordered_set
- 哈希桶的封装部分
- unordered_map的封装
- unordered_set封装
unordered_map&&unordered_set介绍
看名字是和map\set类似的迭代器,和map\set的区别如下:
- map底层是红黑树封装;unordered_map底层是哈希桶
- map支持双向迭代器;unordered_map只支持++
- map是有序的序列;unordered_map是无序的序列
- unordered_map的查找效率比map快
接口方面和map类似,参照map/set的使用即可。
unordered_set
哈希桶的封装部分
哈希桶的代码:哈希桶代码部分参考这里
为了更好适合封装map,在哈希桶里面修改内容如下:
- 添加了迭代器,提供了*,->,++,!=等功能。
- 哈希桶增加了begin和end函数
- 哈希桶insert的返回值修改(为了在map里实现[])
- 添加了const迭代器
具体实现出现的问题,在代码的注释中,有所体现:
namespace HashBucket
{//T表示数据类型template<class T>struct Elem{Elem(const T& data):_data(data), _next(nullptr){}T _data;Elem<T>* _next;};template<class K>struct Hash{size_t operator()(const K& key){return key;}};//特化版本template<>struct Hash<string>{//BKDR哈希*31size_t operator()(const string& s){size_t hash = 0;for (auto e : s){hash += e;hash *= 31;}return hash;}};//迭代器实现//迭代器用到了哈希表,需要前置声明一下(声明不需要写默认传参)template<class K, class T, class KeyOFT, class HashFunc>class HashTables;// K索引, T数据类型, Ref:T的引用 , Ptr:T的指针, KeyOFT:获取T的索引template<class K, class T, class Ref, class Ptr, class KeyOFT, class HashFunc = Hash<K>>struct _HashIterator{typedef Elem<T> Node;typedef HashTables<K, T, KeyOFT, HashFunc> HT;typedef _HashIterator<K, T, Ref, Ptr, KeyOFT, HashFunc> Self;typedef _HashIterator<K, T, T&, T*, KeyOFT, HashFunc> Iterator;//const迭代器,不在这定义,在哈希表定义//typedef _HashIterator<K, const T, const T&, const T*, KeyOFT, HashFunc> const_Iterator;/*typedef _HashIterator<K, T, const T&, const T*, KeyOFT, HashFunc> const_Iterator;*/Node* _node;const HT* _ht; //迭代器还需要哈希表,因为要换桶,没有表无法完成换桶//构造 begin要用,所以要publicpublic:_HashIterator(Node* node, const HT* ht):_node(node),_ht(ht){}//如果是普通迭代器,就是拷贝构造//如果是const,就是构造函数//_HashIterator(const Iterator& it)// :_node(it._node)// , _ht(it._ht)//{}public:Ptr operator->(){return &(_node->_data);}//没有形参//Ref operator->(const Node* node)Ref operator*(){return _node->_data;}// !=号的迭代器注意一下bool operator!=(const Self& s){return _node != s._node;}Self& operator++(){//next不为空,下一个结点就是nextif (_node->_next){_node = _node->_next;return *this;}KeyOFT kot;HashFunc hash;//首先确认桶号size_t hashi = hash(kot(_node->_data)) % _ht->_tables.size();++hashi;while (hashi<_ht->_tables.size()) //小于表长{if (_ht->_tables[hashi]){_node = _ht->_tables[hashi]; //找到了break;}hashi++;}if (hashi == _ht->_tables.size()) //没找到非空的结点.说明是最后一个{_node = nullptr;}return *this;}};//K表示查找值时的索引, T表示数据类型,KeyOFT获取T的索引K,HashFunc计算Hash的函数template<class K, class T, class KeyOFT, class HashFunc = Hash<K>>class HashTables{//迭代器要访问私有的_tables,得是友元template<class K, class T, class Ref, class Ptr, class KeyOFT, class HashFunc>friend struct _HashIterator;typedef Elem<T> Node; typedef T Data;//迭代器的typedef,//unordered_map的public:typedef _HashIterator<K, T, T&, T*, KeyOFT, HashFunc> iterator;//const 迭代器typedef _HashIterator<K, T, const T&, const T*, KeyOFT, HashFunc> const_iterator;public://此版本不太好//Iterator begin()//{// Node* cur = nullptr;// for (auto e : _tables)// {// if (e)// {// return Iterator(e, this);// }// }//}iterator begin(){Node* cur = nullptr;for (auto e : _tables){if (e){cur = e;break;}}return iterator(cur, this);}iterator end(){return iterator(nullptr, this);}const_iterator begin() const{Node* cur = nullptr;for (auto e : _tables){if (e){cur = e;break;}}return const_iterator(cur, this);}const_iterator end() const{return const_iterator(nullptr, this);}pair<iterator, bool> insert(const Data& data){//负载因子过大就扩容CheckCapacity();HashFunc hash;KeyOFT kot;iterator it = find(kot(data));//if (it._node) //查到了,直接返回,不能访问迭代器的成员变量if(it!=end()){/*return make_pair(iterator(cur,this), false);*/return make_pair(it, false);}//散列地址size_t hashi = hash(kot(data)) % _tables.size();//直接插入头插Node* newNode = new Node(data);newNode->_next = _tables[hashi];_tables[hashi] = newNode;n++;return make_pair(iterator(newNode, this), true);}iterator find(const K& key){HashFunc hash;KeyOFT kot;size_t hashi = hash(key) % _tables.size(); Node* cur = _tables[hashi];while (cur){if (kot(cur->_data) == key){return iterator(cur, this);}cur = cur->_next;}return end();}bool erase(const K& key){HashFunc hash;KeyOFT kot;size_t hashi = hash(key) % _tables.size();Node* cur = _tables[hashi];Node* prev = nullptr;while (cur){if (kot(cur->_data) == key){if (!prev) //prev为空,说明是在表上{_tables[hashi] = cur->_next;}else{prev->_next = cur->_next;}delete cur;return true;}prev = cur;cur = cur->_next;}return false;}private://扩容有问题,不应该插入一个就扩容,插入一个就扩容!!!!!!!!!!!!void CheckCapacity(){KeyOFT kot;HashFunc hash;//表为空或者负载因子大于0.7了就扩容//if (!_tables.size() || ((n * 10) / (_tables.size() * 10) > 7))/*if (!_tables.size() || ((n * 10) / _tables.size() > 7))*/if (n == _tables.size()){size_t newCapacity = (_tables.size() == 0 ? 10 : _tables.size() * 2);HashTables* newHashTable = new HashTables;newHashTable->_tables.resize(newCapacity);//将旧表的数据重新映射到新表//遍历旧表for (auto& e : _tables){if (e) //不空,就有数据{Node* cur = e;Node* next; //下一个指针while (cur){size_t hashi = hash(kot(cur->_data)) % newCapacity;next = cur->_next; //暂存下一个cur->_next = newHashTable->_tables[hashi];newHashTable->_tables[hashi] = cur; //头插//newHashTable->n++;cur = next;}}}//swap(*newHashTable, *this);_tables.swap(newHashTable->_tables);}}private:vector<Node*> _tables;size_t n = 0;};
}
unordered_map的封装
- 迭代器类中只实现基本的操作不实现begin/end,哈希桶表提供begin/end,哈希桶里面通过typedef实现const迭代器的规则;
- map外面暴露了三个模板参数,可以自定义hash函数
- 支持[]取数据
- 注意成员变量定义方法
namespace xty
{template<class K>struct Hash{size_t operator()(const K& key){return key;}};//特化版本template<>struct Hash<string>{//BKDR哈希*31size_t operator()(const string& s){size_t hash = 0;for (auto e : s){hash += e;hash *= 31;}return hash;}};template<class K, class V,class HashFunc=Hash<K>>class unordered_map{struct MyKeyOFT; //声明一下//迭代器重命名public:typedef typename HashBucket::HashTables<K, pair<const K, V>, MyKeyOFT, HashFunc>::iterator iterator;typedef typename HashBucket::HashTables<K, pair<const K, V>, MyKeyOFT, HashFunc>::const_iterator const_iterator;private://只有自己知道自己的数据类型是什么样struct MyKeyOFT{const K& operator()(const pair<K, V>& data){return data.first;}};public:iterator begin(){return _hash.begin();}iterator end(){return _hash.end();}//要让map支持[],需要将insert的返回函数修改一下V& operator[](const K& key){pair<iterator, bool> ret = Insert(make_pair(key, V()));return ret.first->second;}//插入pair<iterator, bool> Insert(const pair<K, V>& data){return _hash.insert(data);}//删除bool Erase(const K& key){return _hash.erase(key);}pair<K, V>* Find(const K& key){return _hash.find(key);}private:HashBucket::HashTables<K, pair<const K, V>, MyKeyOFT, HashFunc> _hash;//HashBucket::HashTables<K, V, MyKeyOFT> _hash; 注意要传pair ,不然insert报错};void unordered_map_test(){int a[]= { 11, 22, 33, 44, 55, 66, 77, 88, 99, 111, 444, 555,1111 };unordered_map<int, int> map;for (auto e : a){map.Insert(make_pair(e, e));}}
}
添加一段测试代码检验第三个模板参数的正确性:
class Date{friend struct HashDate;public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}bool operator<(const Date& d)const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}bool operator==(const Date& d) const{return _year == d._year&& _month == d._month&& _day == d._day;}friend ostream& operator<<(ostream& _cout, const Date& d);private:int _year;int _month;int _day;};ostream& operator<<(ostream& _cout, const Date& d){_cout << d._year << "-" << d._month << "-" << d._day;return _cout;}struct HashDate{size_t operator()(const Date& d){size_t hash = 0;hash += d._year;hash *= 31;hash += d._month;hash *= 31;hash += d._day;hash *= 31;return hash;}};// 一个类型要做unordered_map/unordered_set的key,要满足支持转换成取模的整形 和 ==比较// 一个类型要做map/set的key,要满足支持<比较/*if (cur->key < key){}else if (key < cur->key){}else{}*/void test_unordered_map4(){Date d1(2023, 3, 13);Date d2(2023, 3, 13);Date d3(2023, 3, 12);Date d4(2023, 3, 11);Date d5(2023, 3, 12);Date d6(2023, 3, 13);Date a[] = { d1, d2, d3, d4, d5, d6 };unordered_map<Date, int, HashDate> countMap;for (auto e : a){countMap[e]++;}for (auto& kv : countMap){cout << kv.first << ":" << kv.second << endl;}}
unordered_set封装
- 注意成员变量的参数不加const K
- typename
template<class K, class HashFunc=Hash<K>>class unordered_set{struct SetOFT;public://外面要使用迭代器,所以放publictypedef typename HashBucket::HashTables<K, K, SetOFT, HashFunc>::const_iterator iterator;typedef typename HashBucket::HashTables<K, K, SetOFT, HashFunc>::const_iterator const_iterator;public:struct SetOFT{const K& operator()(const K& key){return key;}};iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}const_iterator begin() const{return _ht.begin();}iterator end() const{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://不加pair<K,K>/*HashBucket::HashTables<K, pair<K, K>, SetOFT, HashFunc>*//*HashBucket::HashTables<K, const K, SetOFT, HashFunc> _ht;*/HashBucket::HashTables<K, K, SetOFT, HashFunc> _ht;};