赞
踩
目录
1.2.2operator*() && operator->()
1.2.5operator==() && operator!=()
二、如何用哈希表搭配unordered_map和unordered_set(仿函数)
三、哈希表封装unordered_map和unordered_set(简易版)
3.2unordered_map的模拟实现(My_Unordered_Map.h)
3.3unordered_set的模拟实现(My_Unordered_Set.h)
上一篇章,学习了unordered系列容器的使用,以及哈希结构,那么这一篇章将通过哈希结构来封装unordered系列容器,来进一步的学习他们的使用以及理解为何是如此使用。其实,哈希表的封装方式和红黑树的封装方式形式上是差不多的,如果有了红黑树的封装理解经验,我相信在理解哈希封装的过程会减少负担的。当然,在使用哈希结构中采用的是更具代表的哈希桶,接下来进行封装。
同理模板参数列表的改造跟红黑树的改造差不多。
K:关键码类型
V:不同容器v的容器类型不同,如果是unordered_map,v代表一个键值对,如果是unordered_set,v为k
KeyOfT:因为v的类型不同,通过keyOfT取key的方式就不同 。
Hash:哈希函数仿函数对象类型,哈希函数使用除留余数法,需要将字符串类型转化为整形数字才能取模。
- //前置声明,迭代器中才能定义HashTable对象
- template<class K, class T, class KeyOfT, class hash>
- class HashTable;
-
- template<class K,class T,class Ref,class Ptr,class KeyOfT,class hash = HashFunc<K>>
- struct _HTIterator
- {
- typedef _HTIterator<K,T, Ref, Ptr,KeyOfT,hash> Self;//方便作为接收迭代器操作的返回值的类型
- typedef HashNode<T> Node;
- typedef Node* PNode;
-
- PNode _node;//定义节点指针,方便访问其成员来完成迭代器的操作
- HashTable<K, T, KeyOfT, hash>* _ht;//定义哈希表指针,方便使用其成员来完成迭代器的操作
- size_t _hashi;//记录迭代器访问的位置
-
- _HTIterator(PNode node, HashTable<K, T, KeyOfT, hash>* ht,size_t hashi)
- :_node(node)
- ,_ht(ht)
- ,_hashi(hashi)
- {}
-
- //....
- };
- Ref operator*()
- {
- return _node->_data;
- }
-
- Ptr operator->()
- {
- return &(_node->_data);
- }
- Self& operator++()
- {
- if (_node->_next)//下一个元素不为空
- {
- _node = _node->_next;
- }
- else
- {
- /*hash hs;
- KeyOfT oft;
- int hashi = hs(oft(this->_data) % _tables.size());*/
- //寻找下一个不为空的桶
- ++_hashi;
- while(_hashi < _ht->_tables.size())
- {
- if (_ht->_tables[_hashi] != nullptr)
- {
- _node = _ht->_tables[_hashi];
- break;
- }
- ++_hashi;
- }
- //到末尾还没有找到不为空的桶
- if(_hashi == _ht->_tables.size())
- _node = nullptr;
-
- }
- return *this;
-
- }
- Self operator--()
- {
- PNode cur = _ht->_tables[_hashi];
-
- //当前节点就是第一个节点
- if (cur->_next == nullptr)
- {
- //寻找上一个非空桶
- --_hashi;
- while (_hashi)
- {
- //寻找该非空桶的最后一个元素
- if (_ht->_tables[_hashi])
- {
- cur = _ht->_tables[_hashi];
- while (cur->_next)
- {
- cur = cur->_next;
- }
- _node = cur;
- break;
- }
- _hashi--;
- }
-
- }
- else
- {
- while (cur->_next != _node)
- {
- cur = cur->_next;
- }
- _node = cur;
- }
- return *this;
- }
- bool operator==(const Self& x)
- {
- return _node == x._node;//由于没有重复的元素,直接比较节点的地址
- }
-
- bool operator!=(const Self& x)
- {
-
- return _node != x._node;
- }
我们可以用两张哈希表分别封装一份unordered_map和一份unordered_set,但是这样做的效果就带来了代码冗余。为了减少代码冗余,模拟跟库保持用一张哈希表封装unordered_map和unordered_set,但是该如何做到套用一张表呢,我们来进一步分析。
首先对于unordered_map而言,其存放的节点值是pair,而对于unordered_set存放的是key,这对于哈希表节点的实现到是没啥问题,但是对于哈希表内部的构造,是需要查询插入的位置,就需要进行比较,若将比较实现成key的比较,那么对于pair类型又该如何比较,虽然知道比较的也是pair中的key,但是如何做到既满足unordered_set中的key类型比较,又满足pair类型中的key比较,总不能干两份代码吧。这个时候,我们的仿函数又派上用场了,对于unordered_set和unordered_map中都构造一个仿函数,分别表示取到unordered_set的key,和unordered_map中pair中的key,那么哈希表中的比较,就可以换成仿函数的比较,当往unordered_set中插入元素进行比较,调用的就是unordered_set的仿函数,当往unordered_map中插入元素进行比较,调用的就是unordered_map的仿函数从而达到回调。用一张图来进行表示,如图:
- //哈希桶/链地址法
- namespace Hash_Bucket
- {
- template<class T>
- struct HashNode
- {
- HashNode(const T& data)
- :_next(nullptr)
- ,_data(data)
- {}
- HashNode<T>* _next;
- T _data;
- };
-
- //前置声明,迭代器中才能定义HashTable对象
- template<class K, class T, class KeyOfT, class hash>
- class HashTable;
-
- template<class K,class T,class Ref,class Ptr,class KeyOfT,class hash = HashFunc<K>>
- struct _HTIterator
- {
- typedef _HTIterator<K,T, Ref, Ptr,KeyOfT,hash> Self;
- typedef HashNode<T> Node;
- typedef Node* PNode;
-
- PNode _node;
- HashTable<K, T, KeyOfT, hash>* _ht;
- size_t _hashi;
- _HTIterator(PNode node, HashTable<K, T, KeyOfT, hash>* ht,size_t hashi)
- :_node(node)
- ,_ht(ht)
- ,_hashi(hashi)
- {}
-
- Self& operator++()
- {
- if (_node->_next)
- {
- _node = _node->_next;
- }
- else
- {
- /*hash hs;
- KeyOfT oft;
- int hashi = hs(oft(this->_data) % _tables.size());*/
-
- ++_hashi;
- while(_hashi < _ht->_tables.size())
- {
- if (_ht->_tables[_hashi] != nullptr)
- {
- _node = _ht->_tables[_hashi];
- break;
- }
- ++_hashi;
- }
- if(_hashi == _ht->_tables.size())
- _node = nullptr;
-
- }
- return *this;
-
- }
-
-
-
- Self operator--()
- {
- PNode cur = _ht->_tables[_hashi];
-
- //当前节点就是第一个节点
- if (cur->_next == nullptr)
- {
- //寻找上一个非空桶
- --_hashi;
- while (_hashi)
- {
- //寻找该非空桶的最后一个元素
- if (_ht->_tables[_hashi])
- {
- cur = _ht->_tables[_hashi];
- while (cur->_next)
- {
- cur = cur->_next;
- }
- _node = cur;
- break;
- }
- _hashi--;
- }
-
- }
- else
- {
- while (cur->_next != _node)
- {
- cur = cur->_next;
- }
- _node = cur;
- }
- return *this;
- }
-
- Ref operator*()
- {
- return _node->_data;
- }
-
- Ptr operator->()
- {
- return &(_node->_data);
- }
-
- bool operator==(const Self& x)
- {
- return _node == x._node;//由于没有重复的元素,直接比较节点的地址
- }
-
- bool operator!=(const Self& x)
- {
-
- return _node != x._node;
- }
-
- };
-
- template<class K,class T,class KeyOfT,class hash>
- class HashTable
- {
- //声明友元,迭代器方可访问该类中的私有成员
- template<class K, class T, class Ref, class Ptr, class KeyOfT,class hash>
- friend struct _HTIterator;
- public:
- typedef _HTIterator<K, T,T&, T*, KeyOfT,hash> iterator;
- typedef _HTIterator<K, T, const T&, const T*, KeyOfT, hash> const_iterator;
-
- typedef HashNode<T> Node;
- typedef Node* PNode;
-
- HashTable(size_t size = 10)
- {
- _tables.resize(size);
- }
-
- iterator begin()
- {
- for (int i = 0; i < _tables.size(); i++)
- {
- if (_tables[i] != nullptr)
- return iterator(_tables[i], this, i);
- }
- return end();
-
- }
- iterator end()
- {
- return iterator(nullptr,this,-1);
- }
-
- const_iterator begin() const
- {
- for (int i = 0; i < _tables.size(); i++)
- {
- if (_tables[i] != nullptr)
- return iterator(_tables[i], this, i);
- }
- return end();
-
- }
- const_iterator end() const
- {
- return iterator(nullptr, this, -1);
- }
-
- iterator Find(const K& key)
- {
- KeyOfT oft;
- hash hs;
- int hashi = hs(key) % _tables.size();
-
-
- PNode cur = _tables[hashi];
- while (cur)
- {
- if(hs(oft(cur->_data)) == hs(key))
- return iterator(cur, this, hashi);
-
- cur = cur->_next;
- }
- return iterator(nullptr, this, -1);
- }
-
- pair<iterator,bool> Insert(T data)
- {
- hash hs;
- KeyOfT oft;
- iterator it = Find(oft(data));
- if (it != end())
- return make_pair(it,false);
-
-
-
- //扩容
- if (_n == _tables.size())
- {
- vector<PNode> _newHT(_tables.size()*2);
-
- for (int i = 0; i < _tables.size(); i++)
- {
-
- PNode cur = _tables[i];
- while (cur)
- {
- PNode next = cur->_next;
-
- int hashi = hs(oft(_tables[i]->_data)) % _newHT.size();
-
- //元素一直在变,所以不能用_tables[i]做代表
- /*_tables[i]->_next = nullptr;
- _newHT[hashi] = _tables[i];*/
-
- cur->_next = nullptr;
- _newHT[hashi] = cur;
-
- cur = next;
- }
- _tables[i] = nullptr;
- }
- _tables.swap(_newHT);
- }
- //头插
- PNode newnode =new Node(data);
- int hashi = hs(oft(data)) % _tables.size();
- newnode->_next = _tables[hashi];
- _tables[hashi] = newnode;
- ++_n;
-
- return make_pair(iterator(newnode, this, hashi),true);
-
- }
-
- bool Erase(const K& key)
- {
- KeyOfT oft;
- iterator it = Find(key);
- if (it == end())
- return false;
- hash hs;
- int hashi = hs(key) % _tables.size();
- PNode cur = _tables[hashi];
- PNode parent = nullptr;
- while (hs(oft(cur->_data)) != hs(key))
- {
- parent = cur;
- cur = cur->_next;
- }
- if (parent)
- {
- delete cur;
- parent->_next = nullptr;
- }
- else
- {
- delete cur;
- _tables[hashi] = nullptr;
- }
- return true;
- }
-
- ~HashTable()
- {
- for (int i = 0; i < _tables.size(); i++)
- {
- PNode cur = _tables[i];
- while (cur)
- {
- PNode next = cur->_next;
-
- delete cur;
-
- cur = next;
- }
- _tables[i] = nullptr;
- }
- }
-
-
-
- //另外加的,为了测试用
- void Some()
- {
- size_t bucketSize = 0;
- size_t maxBucketLen = 0;
- size_t sum = 0;
- double averageBucketLen = 0;
-
- for (size_t i = 0; i < _tables.size(); i++)
- {
- Node* cur = _tables[i];
- if (cur)
- {
- ++bucketSize;
- }
-
- size_t bucketLen = 0;
- while (cur)
- {
- ++bucketLen;
- cur = cur->_next;
- }
-
- sum += bucketLen;
-
- if (bucketLen > maxBucketLen)
- {
- maxBucketLen = bucketLen;
- }
- }
-
- averageBucketLen = (double)sum / (double)bucketSize;
-
- printf("all bucketSize:%d\n", _tables.size());
- printf("bucketSize:%d\n", bucketSize);
- printf("maxBucketLen:%d\n", maxBucketLen);
- printf("averageBucketLen:%lf\n\n", averageBucketLen);
- }
- private:
- vector<HashNode<T>*> _tables;
- size_t _n = 0;
- };
-
- }
- #pragma once
-
- #include "HashTable.h"
-
-
- namespace Hash_Bucket
- {
- template<class K,class V,class Hash = HashFunc<K>>
- class unordered_map
- {
- public:
- struct KeyOfTMap
- {
- const K& operator()(const pair<const K, V>& data)
- {
- return data.first;
- }
- };
- typedef typename HashTable<K, pair<const K,V>, KeyOfTMap, Hash>::iterator iterator;
- typedef typename HashTable<K, pair<const K,V>, KeyOfTMap, Hash>::const_iterator const_iterator;
-
-
- iterator begin()
- {
- return _hs.begin();
- }
- iterator end()
- {
- return _hs.end();
- }
-
- const_iterator begin() const
- {
- return _hs.begin();
- }
- const_iterator end() const
- {
- return _hs.end();
- }
-
- pair<iterator, bool> insert(const pair<K,V>& data)
- {
- return _hs.Insert(data);
- }
- V& operator[](const K& key)
- {
- pair<iterator, bool> ret = _hs.Insert(make_pair(key,V()));
- return ret.first->second;
- }
-
- bool erase(const K& key)
- {
- return _hs.Erase(key);
- }
-
- iterator find(const K& key)
- {
- return _hs.Find(key);
- }
-
- private:
- HashTable<K, pair<const K,V>, KeyOfTMap, Hash> _hs;
- };
-
- void test_map()
- {
- unordered_map<string, string> dict;
- dict.insert(make_pair("sort", ""));
- dict.insert(make_pair("string", ""));
- dict.insert(make_pair("insert", ""));
-
- for (auto& kv : dict)
- {
- //kv.first += 'x';
- kv.second += 'x';
-
- cout << kv.first << ":" << kv.second << endl;
- }
- cout << endl;
-
- string arr[] = { "你", " ","好", " ", "吗", " ", "你", "吃", " ", "了", "饭", "吗", "?" };
- unordered_map<string, int> count_map;
- for (auto& e : arr)
- {
- count_map[e]++;
- }
-
- for (auto& kv : count_map)
- {
- cout << kv.first << ":" << kv.second << endl;
- }
- cout << endl;
- }
- }
- #pragma once
-
- #include "HashTable.h"
-
-
- namespace Hash_Bucket
- {
- template<class K,class Hash = HashFunc<K>>
- class unordered_set
- {
- public:
- struct KeyOfTSet
- {
- const K& operator()(const K& key)
- {
- return key;
- }
- };
- typedef typename HashTable<K, const K, KeyOfTSet, Hash>::iterator iterator;
- typedef typename HashTable<K, const K, KeyOfTSet, Hash>::const_iterator const_iterator;
-
-
- iterator begin()
- {
- return _hs.begin();
- }
- iterator end()
- {
- return _hs.end();
- }
-
- const_iterator begin() const
- {
- return _hs.begin();
- }
- const_iterator end() const
- {
- return _hs.end();
- }
-
- pair<iterator, bool> insert(const K& key)
- {
- return _hs.Insert(key);
- }
-
-
- bool erase(const K& key)
- {
- return _hs.Erase(key);
- }
-
- iterator find(const K& key)
- {
- return _hs.Find(key);
- }
- void some()
- {
- _hs.Some();
- }
- private:
- HashTable<K, const K, KeyOfTSet, Hash> _hs;
- };
-
- void test_set()
- {
-
- unordered_set<int> us;
- us.insert(5);
- us.insert(15);
- us.insert(52);
- us.insert(3);
-
- unordered_set<int>::iterator it = us.begin();
- while (it != us.end())
- {
- //*it += 5;
- cout << *it << " ";
- ++it;
- }
- cout << endl;
-
- for (auto e : us)
- {
- cout << e << " ";
- }
- cout << endl;
- us.some();
- }
-
- }
- #include "My_Unordered_Map.h"
- #include "My_Unordered_Set.h"
-
- int main()
- {
-
- Hash_Bucket::test_map();
- Hash_Bucket::test_set();
-
- return 0;
- }
输出结果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。