赞
踩
我们先来看一段代码。
我们知道unordered_set和unordered_map的底层是用哈希表实现的
,而unordered_set的数据key值,unordered_map的数据是key和value的键值对。从上图的代码中可以看出,哈希表的节点可以支持存unordered_map的数据,但是unordered_set的数据呢?难道我们要再实现一个哈希表,它的节点里面存的是key吗?其实也不是不可以,但是这样大部分代码就冗余了不好。那么我们该怎样改造哈希表呢?
我们可以进行以上代码的改造,我们的底层哈希表确实不知道,要存的是key的数据,还是key和value的键值对数据,所以我们直接让哈希表节点的模板参数<class K,class V>变成<class T>,哈希表的模板参数也随之改变
这样底层的哈希表什么类型都可以接受,而这时就需要我们上层的unordered_set和unordered_map显示的传数据类型过去。
大家可能也看到了哈希表模板参数的后两个参数KeyOfT和HashFunc这里我们先不介绍,下面用到了我们在介绍。
我们先来看看模板参数改造后,原来的插入代码会暴露出哪些问题?
首先第一个问题就是参数问题
模板参数改造后存的数据类型已经不是键值对了,而是T类型。
接下来就迎来了第二个问题,插入位置计算问题。
我们知道插入数据时,要计算数据在哈希表中对应的坐标。那么改造后的T类型数据我们能不能直接进行计算呢?传过来的要是key类型我们可以进行计算,但是传过来的要是键值对呢?那么我们这时就需要增加一层仿函数
。我们的底层哈希表确实不知道传的是什么类型的数据,能不能用除留余数法进行计算。但是我们的封装层unordered_set和unordered_map知道,我们显示的传仿函数过去就可以,不同类型的数据,就调用不同类型的仿函数。
这里就是哈希表第三模板参数KeyOfT的作用。
蓝色框里面的hf是另一个问题,我们接下来再解答。
到这里我们又遇到一个问题,这里我们数据类型如果是整形我们可以用除留余数法计算坐标,但是如果是浮点型和字符串类型
呢?我们该如何计算?
这里我们就要用到仿函数,用仿函数把数据间接或者直接转化成整形
。
删除和查找并没有什么其它特别的改造,主要面临的问题和插入几乎一致所以就不过多讲述。主要放代码让大家对比他们之间的区别。
到这哈希表本身的改造就完成了,我们还有一个重要的任务就是增加哈希表的迭代器。
这里我们可以知道,哈希表的迭代器里面有两个成员变量,一个是节点指针,一个是哈希表指针
。那么这里就有一个问题了,为什么要多存一个哈希表指针呢?有什么用呢?其实这个哈希表的指针用处巨大,因为我们要用迭代器最重要的是要对迭代器进行++或者--
,没有哈希表我们要对哈希表的节点进行++或者–该如何进行呢?我们如何迭代的往下走?几乎没有办法,我们没有哈希表指针寸步难行。所以这里我们多加了一个哈希表指针。
哈希表节点++的思想:
首先先看一下被++节点的后面还有没有数据,如果有那么下一个访问的节点就是它。
如果没有就继续往后找不为空的桶,如果找到了那么下一个访问的就是这个不为空桶的第一个节点。如果没有找到,那么就说明所有节点都访问完毕了。
举个例子:
我们假设it是迭代器指针的它指向11,it++后该指向的节点是谁呢?是21。
如果it指向31呢?
it++后该指向的节点是谁呢?是3。
就是应用这样的思路进行++。
代码实现如下:
后置++和前置++思路一样。这里就不过多介绍。只不过是返回值的问题。大家有兴趣可以自己去了解一下
–的思路和++差不多,只不过–是从尾部开始往前走,++是从头部开始往后走。大家可以自己去尝试实现一下。
#pragma once #include <vector> template<class K> struct Hash { size_t operator()(const K& key) { return key; } }; template<> struct Hash<string> { size_t operator()(const string& s) { size_t value = 0; for (auto& ch : s) { value += ch; } return value; } }; namespace LinkHash { 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 Ref, class Ptr, class KeyOfT, class HashFunc> struct HTIterator { typedef HashNode<T> Node; typedef HTIterator<K, T, Ref, Ptr, KeyOfT, HashFunc> Self; Node* _node; HashTable<K, T, KeyOfT, HashFunc>* _pht; HTIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht) :_node(node) ,_pht(pht) {} Ref operator*() { return _node->_data; } Ptr operator->() { return &_node->_data; } bool operator==(const Self& it) { return _node == it._node; } bool operator!=(const Self& it) { return _node != it._node; } Self& operator++() { Node* cur = _node->next; if (cur) { _node = cur; } else { KeyOfT kot; HashFunc hf; size_t index = hf(kot(_node->_data)) % _pht->_tables.size(); index++; while (index < _pht->_tables.size() && !_pht->_tables[index]) { index++; } if (index < _pht->_tables.size()) _node = _pht->_tables[index]; else _node = nullptr; } return *this; } Self operator++(int) { Node* record = _node; Node* cur = _node->next; if (cur) { _node = cur; } else { KeyOfT kot; HashFunc hf; size_t index = hf(kot(_node->_data)) % _pht->_tables.size(); index++; while (index < _pht->_tables.size() && !_pht->_tables[index]) { index++; } if (index < _pht->_tables.size()) _node = _pht->_tables[index]; else _node = nullptr; } return HTIterator(record,_pht); } }; template<class K, class T, class KeyOfT, class HashFunc> class HashTable { typedef HashNode<T> Node; template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc> friend struct HTIterator; typedef HashTable<K, T, KeyOfT, HashFunc> Self; public: typedef HTIterator<K, T, T&, T*, KeyOfT, HashFunc> iterator; HashTable() = default; HashTable(const Self& ht) { _tables.resize(ht._tables.size()); for (size_t i = 0; i < ht._tables.size(); i++) { Node* cur = ht._tables[i]; while (cur) { Node* newnode = new Node(cur->_data); Node* next = _tables[i]; _tables[i] = newnode; newnode->next = next; cur = cur->next; } } } Self& operator=(Self ht) { swap(_size, ht._size); _tables.swap(ht._tables); return *this; } ~HashTable() { for (size_t i = 0; i < _tables.size(); i++) { Node* cur = _tables[i]; while (cur) { Node* next = cur->next; delete cur; cur = next; } _tables[i] = nullptr; } } iterator begin() { size_t index = 0; while (index < _tables.size() && !_tables[index]) { index++; } return iterator(_tables[index], this); } iterator end() { return iterator(nullptr, this); } bool Erase(const K& key) { if (_size == 0) { return false; } HashFunc hf; KeyOfT kot; size_t index = hf(key) % _tables.size(); Node* cur = _tables[index]; Node* prev = nullptr; while (cur) { if (kot(cur->_data) == key) { Node* next = cur->next; if(prev) prev->next = next; else _tables[index] = next; delete cur; cur = nullptr; _size--; return true; } else { prev = cur; cur = cur->next; } } return false; } Node* Find(const K& key) { if (_size == 0) { return nullptr; } HashFunc hf; KeyOfT kot; size_t index = hf(key) % _tables.size(); Node* cur = _tables[index]; while (cur) { if (kot(cur->_data) == key) { return cur; } else { cur = cur->next; } } return nullptr; } bool Insert(const T& data) { KeyOfT kot; Node* ret = Find(kot(data)); if (ret) { return false; } if (_tables.size() == 0 || _size*10 / _tables.size() == 10) { size_t newsize = _tables.size() == 0 ? 10 : 2 * _tables.size(); HashTable<K,T,KeyOfT,HashFunc> newHT; newHT._tables.resize(newsize); HashFunc hf; for (int i = 0; i < _tables.size(); i++) { Node* cur = _tables[i]; while (cur) { Node* next = cur->next; size_t index = hf(kot(cur->_data)) % newHT._tables.size(); if (newHT._tables[index]) { Node* next = newHT._tables[index]; newHT._tables[index] = cur; cur->next = next; } else { newHT._tables[index] = cur; newHT._tables[index]->next = nullptr;; } newHT._size++; cur = next; } _tables[i] = nullptr; } _tables.swap(newHT._tables); } HashFunc hf; Node* cur = new Node(data); size_t index = hf(kot(data)) % _tables.size(); if (_tables[index]) { Node* next = _tables[index]; _tables[index] = cur; cur->next = next; } else { _tables[index] = cur; } _size++; return true; } private: vector<Node*> _tables; size_t _size = 0; }; }
#pragma once #include"HashTable.h" namespace lzf { template<class K> struct SetKeyOfT { const K& operator()(const K& key) { return key; } }; template<class K> class unorderset { public: typedef typename LinkHash::HashTable<K, K, SetKeyOfT<K>, Hash<K>>::iterator iterator; iterator begin() { return _ht.begin(); } iterator end() { return _ht.end(); } bool insert(const K& key) { return _ht.Insert(key); } private: LinkHash::HashTable<K, K, SetKeyOfT<K>,Hash<K>> _ht; }; void test_unorderset() { unorderset<string> us; us.insert("left"); us.insert("right"); us.insert("map"); us.insert("further"); us.insert("rate"); us.insert("grain"); unorderset<string>::iterator it = us.begin(); while (it != us.end()) { cout << *it << " "; ++it; } cout << endl; unorderset<string> uss = us; unorderset<string>::iterator it1 = uss.begin(); while (it1 != uss.end()) { cout << *it1 << " "; ++it1; } } }
#pragma once #include"HashTable.h" #include<string> namespace lzf { template<class K,class V> struct MapKeyOfT { const K& operator()(const pair<K,V>& kv) { return kv.first; } }; template<class K,class V> class unordermap { public: typedef typename LinkHash::HashTable<K, pair<K,V>, MapKeyOfT<K,V>, Hash<K>>::iterator iterator; iterator begin() { return _ht.begin(); } iterator end() { return _ht.end(); } bool insert(const pair<K,V>& kv) { return _ht.Insert(kv); } private: LinkHash::HashTable<K, pair<K,V>, MapKeyOfT<K,V>, Hash<K>> _ht; }; void testunordermap() { unordermap<string, string> um; um.insert(make_pair("left", "")); um.insert(make_pair("right", "ұ")); um.insert(make_pair("map", "ͼ")); um.insert(make_pair("set", "")); unordermap<string, string>::iterator it = um.begin(); while (it != um.end()) { cout << it->first << ":" << it->second << endl; ++it; } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。