赞
踩
在源码中,这两个STL容器都是共用一个框架的所以,我们就用一个框架实现两个
容器,代码细节都在注释上
HashTable.h
- #pragma once
- #define _CRT_SECURE_NO_WARNINGS 1
- #include <iostream>
- #include <vector>
- #include <string>
- #include <utility>
- #include <algorithm>
- #include <utility>
- //namespace txh
- //{
- // enum state
- // {
- // EMPTY,//空
- // EXITS,//存在
- // DELETE,//被删除
- // };
- // template<class K,class V>//元素类
- // class HashData//先画一个存放数据的蓝图,声明和定义
- // {
- // public:
- // std::pair<K,V> _kv;//哈希表,我们要根据key(也是模板的K)值去模一个值,得到哈希值存到哈希表的位置
- // //V是数据类型
- //
- // //我们在查找的时候不知道用什么表示为空,没有一个合适的数,0吗,-1,不对,因为这些都有可能是某个数0,-1
- // //所以不能用,我们用另一种方法,那就是在每个数据上存一个标志代表是否为空
- //
- // //前面定义了enum类型,类名是state,
- // state _state = EMPTY;//默认都是空
- // };
- //
- //
- //
- // //线性探测法
- // template<class K,class V,class keyConv>
- // class HashTable
- // {
- // public:
- // typedef HashData<K, V> data;
- // keyConv kc;
- // HashTable()
- // :_n(0)
- // {
- // _table.resize(11);
- // }
- //
- // //那如果key值不是int而是string 类型呢,我们不可能去特意在模板里判断一个数是否是string类型
- // //那么我们可以传一个模板,因为我们hash主要的是要把key值转换成hash值
- // //我们用一个模板类去封装转换成hash值的方式
- // bool insert(const std::pair<K,V>& kv)//插入元素,我们用pair类接受
- // {
- // //如果表内有相同的值的话,就不插入了
- // if (find(kv.first))
- // {
- // return false;
- // }
- //
- // if (_n * 10 / _table.size() > 7)//负载因子大于0.7我们的冲突就会变大,导致效率下降
- // //在线性探测中,所以我们就需要扩容,扩多少倍呢,扩质数倍其实是很好的,java中只是扩2倍,没有按质数扩
- // //按质数扩会降低哈希冲突率
- // {
- // HashTable<K, V,keyConv> newtable;
- // newtable._table.resize(2 * _table.size());
- //
- // for (auto& e:_table)//为什么要重新插入,是因为我们扩容之后,下标映射变了,所以要重新插入
- // {
- // if(e._state == EXITS)
- // {
- // /* int hashi = e._kv.first % newtable._table.size();
- // newtable._table[hashi]._kv = e._kv;
- // newtable._table[hashi]._state = e._state;*/
- // newtable.insert(e._kv);//复用写法
- // }
- // }
- // _table.swap(newtable._table);//2023年的现代写法,新表代替了旧表
- // }
- //
- //
- // //线性探测
- // size_t hashi = kc(kv.first) % _table.size(); //成员里不用capacity的原因是我们的vector的下标引用只能引用size内的值,capacity
- // //肯定大于等于size,所以肯定不能去模除capacity
- // size_t i = 0;
- // while (_table[hashi]._state != EMPTY)//查看当前表的每个元素类是否有空,没空一直继续
- // {
- // //如果当前的hashi被占了,直接去下一个厕所看(就是相邻的下一个),所以有了i
- // //i控制每一轮要看的厕所不是上一次看过的,所以i++
- // ++hashi;
- // hashi %= _table.size();//防止越界
- // }
- // _table[hashi]._kv = kv;
- // _table[hashi]._state = EXITS;
- // ++_n;
- // return true;
- // }
- //
- // data* find(const K& key)
- // {
- // size_t hashi = kc(key) % _table.size();
- // size_t i = 0;
- // while (_table[hashi]._state != EMPTY)//这是线性探测
- // {
- // if (_table[hashi]._kv.first == key
- // && _table[hashi]._state == EXITS)
- // {
- // return &_table[hashi];
- // }
- // hashi++;
- // hashi %= _table.size();//防止越界
- // }
- //
- // return nullptr;
- // }
- //
- // bool erase(const K& key)
- // {
- // data* ret = find(key);
- // if (!ret)
- // {
- // return false;
- // }
- // else
- // {
- // ret->_state = DELETE;
- // _n--;
- // return true;
- // }
- // }
- // private:
- // std::vector<data> _table;//当前存放元素的表,
- // size_t _n;//代表当前表内有多少个元素
- // //负载因子是什么呢,就是总共插入的元素 除以 总元素 就是负载因子
- // };
- //}
- //
- template <typename T>
- struct HashFunc
- {
- size_t operator()(const T& key)//键值接受
- {
- size_t hashi = key;
- return hashi;
- }
- };//声明键值转换函数是模板类,因为模板类只是个图纸
-
-
- template <>//特化之前一定要有一个基础类
- struct HashFunc<std::string>
- {
- size_t operator()(const std::string& key)
- {
- size_t hashi = 0;
- for (auto e : key)//关键值转hash
- {
- hashi *= 131;
- hashi += e;
- }
- return hashi;
- }
- };
- namespace HashBucket
- {
-
- //开散列,拉链法
- template<class T>//改造后只有一个参数,我们自动去识别是一个参数还是两个参数
- struct HashNode
- {
- HashNode(const T& data) :_data(data), next(nullptr) {}
- T _data;
- HashNode* next;
- };
- template<class K, class T, class keyCon, class KeyofT>
- class HashTable;//在hashIterator类前面声明,编译器就会跟着这个声明去找到相应的模板
- //容器都有迭代器,所以我们要封装一个迭代器,因为我们的迭代器会有重载*,->这种,所以我们直接传ptr这种吧
-
- template<class K,class T,class Ref,class Ptr,class keyCon,class KeyofT>
- class _HashIterator
- {
- public:
- typedef HashNode<T> node;
- typedef _HashIterator<K, T, T&, T*, keyCon, KeyofT> iterator;
- typedef HashTable<K, T, keyCon, KeyofT> HT;
-
-
- _HashIterator(node* node1,HT* ht) :_node(node1),_ht(ht){}
- _HashIterator(const iterator& it):_node(it._node),_ht(it._ht){}
- typedef _HashIterator<K,T,Ref,Ptr,keyCon,KeyofT> self;
-
- self& operator++()
- {
- //先看当前桶里是否有元素,如果后续有元素,那就在桶里++,如果桶里没有,那么就去其他桶
- //里找
- node* cur = _node->next;
- if (cur)
- {
- _node = _node->next;
- //返回迭代器本身
- }
- else
- {
- //去遍历这个桶,找到后续第一个有元素的桶
- keyCon kc;
- //找到当前迭代器里的节点的桶的位置
- size_t hashi = kc(_node->_data) % _ht->_table.size();
-
- //遍历这个桶
- ++hashi;
- while (hashi < _ht->_table.size())
- {
- if (_ht->_table[hashi])
- {
- _node = _ht->_table[hashi];
- break;
- }
- hashi++;
- }
- if (hashi == _ht->_table.size())
- {
- _node = nullptr;
- }
- }
- return *this;
- }
-
- self operator++(int)
- {
- //后置++
- self ret = self(_node,_ht);
- node* cur = _node;
- if (cur)
- {
- _node = _node->next;
- //返回迭代器本身
- }
- else
- {
- //去遍历这个桶,找到后续第一个有元素的桶
- keyCon kc;
- //找到当前迭代器里的节点的桶的位置
- size_t hashi = kc(_node->_data) % _ht->_table.size();
-
- //遍历这个桶
- ++hashi;
- while (hashi < _ht->_table.size())
- {
- if (_ht->_table[hashi])
- {
- _node = _ht->_table[hashi];
- break;
- }
- hashi++;
- }
- if (hashi == _ht->_table.size())
- {
- _node = nullptr;
- }
- }
- return ret;//返回迭代器对象的时候,会进行拷贝构造,所以不用担心生命周期
- }
-
- bool operator!=(const self& s)
- {
- return s._node != _node;
- }
-
- Ref operator*()
- {
- return _node->_data;
- }
-
- Ptr& operator->()
- {
- return &_node->_data;
- }
- public:
- node* _node;//有了迭代器,我们就可以直接用迭代器,迭代器++,就是这里的_node = _node->next等
- //我们的++,--操作还会设计到遍历桶,我们的遍历桶只在HashTable里面设计了,所以我们需要有一个
- //可以操作HashTable的动作,因为都是算类,我们可以设计一个指针进行操作,我们用友元
- HT* _ht;
- };
-
- //第二个才是数,第一个只是我们的键值
- template<class K, class T, class keyCon,class KeyofT>
- class HashTable
- {
- public:
-
- //友元声明
- template<class K,class T,class Ref,class Ptr,class KeyCon, class KeyofT>//声明这个_hashIterator是个模板类,告诉编译器我要这个_HashIterator模板类
- //是HashTable类的友元
- friend class _HashIterator;
-
-
- typedef _HashIterator<K,T,T&,T*,keyCon,KeyofT> iterator;
- typedef _HashIterator<K, T,const T&,const T*,keyCon,KeyofT> const_iterator;
- //我们通过传的模板参数来控制我们的迭代器是否是const迭代器
-
- typedef HashNode<T> node;
- keyCon hash;
- KeyofT koft;
- size_t _stl_next_prime(size_t size)
- {
- 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
- };
- //这里存放素数,为什么设素数为长度呢,因为在模素数的
- //时候,造成的哈希冲突会竟可能的好
-
- for (int i = 0;i < __stl_num_primes;i++)
- {
- if (__stl_prime_list[i] > size)
- {
- return __stl_prime_list[i];
- }
- }
- }
- HashTable() :_n(0)
- {
- _table.resize(_stl_next_prime(_table.size()));
- }
-
- ~HashTable()
- {
- for (auto e : _table)
- {
- node* cur = e;
- while (cur)//把在桶里的元素删除
- {
- node* next = cur->next;
- delete cur;
- cur = next;
- }
- e = nullptr;
- }
- }
-
- iterator begin()
- {
- node* cur = nullptr;
- //找到第一个有元素的桶
- for (size_t hashi = 0;hashi < _table.size();hashi++)
- {
- cur = _table[hashi];
- if (cur)
- {
- break;
- }
- }
- return iterator(cur,this);//传值返回,生命周期就不用管,他会执行一次拷贝构造
- //匿名构造函数
- }
-
- iterator end()
- {
- return iterator(nullptr, this);//传值返回,生命周期就不用管,他会执行一次拷贝构造
- }
-
- const_iterator begin() const
- {
- node* cur = nullptr;
- for (size_t i = 0; i < _table.size(); ++i)
- {
- cur = _table[i];
- if (cur)
- {
- break;
- }
- }
-
- return const_iterator(cur, this);
- }
-
- const_iterator end() const{return const_iterator(nullptr, this);}
-
- std::pair<iterator,bool> insert(const T& data)
- {
- iterator ret = find(koft(data));
- if (ret != end())//有该点的话
- {
- return std::make_pair(ret, true);
- }
- //拉链法中,当负载因子等于1的时候,哈希的冲突就会变大,桶里的元素可能最多,所以负载因子为1就扩容最好
- if (_n == _table.size())//负载因子等于1
- {
- /*HashTable<K, V, keyCon> newHash;*/
- /*newHash._table.resize(_table.size() * 2);*/
- //这种做法很费效率,因为每次插入要重新new一个节点,并释放,所以同样映射的节点就不需要重新new了
- std::vector<node*> newtable;
- /*newtable.resize(_table.size() * 2,nullptr);*/
- //扩容
- newtable.resize(_stl_next_prime(_table.size()));
- //重新映射
- for (auto& e : _table)
- {
- node* cur = e;
- //头插
- while (cur)
- {
- node* next = cur->next;
-
- //哈希重新映射
- size_t hashi = hash(koft(cur->_data)) % newtable.size();
- cur->next = newtable[hashi];//将未插入之前的头插到cur的next
- newtable[hashi] = cur;
- cur = next;
- }
- e = nullptr;
- }
- //现代写法
- _table.swap(newtable);//旧表换新表,只是表新表的内容搬到旧表中
- //新表不用手动释放,生命周期只在当前作用域中,如果出了作用域会自动销毁(调用析构函数)
- }
-
- //这里的kc是实例化后的对象,所以可以kc(kv.first),但是不能用hash(kv.first),因为hash不是一个实例化的对象,需要把他实例化
- //之后才能调用
- size_t hashi = hash(koft(data)) % _table.size();
-
- //头插
- node* newnode = new node(data);
- newnode->next = _table[hashi];
- _table[hashi] = newnode;
- ++_n;
- return std::make_pair(iterator(newnode,this),true);
- }
-
- iterator find(const K& key)
- {
- size_t hashi = hash(key) % _table.size();//根据关键值算出哈希值
- node* cur = _table[hashi];
-
- //在桶里寻找
- while (cur)
- {
- if (koft(cur->_data) == key)
- {
- return iterator(cur,this);
- }
- cur = cur->next;
- }
- return iterator(nullptr,this);
- }
-
- bool erase(const K& key)
- {
- size_t hashi = hash(key) % _table.size();
- node* cur = _table[hashi];//找到了桶
- node* prev = nullptr;
- while (cur)
- {
- if (cur->_kv.first == key)
- {
- //如果是头节点的话
- if (cur->_kv == _table[hashi])
- {
- _table[hashi] = cur->next;
- }
- else//找到的这个key值不是头结点的话
- {
- prev->next = cur->next;
- }
- delete cur;
- --_n;
- return true;
- }
- else//继续找
- {
- prev = cur;
- cur = cur->next;
- }
- }
- return false;
- }
- private:
- std::vector<node*> _table;//每个位置存放hashnode类,里面有指针,存放的是头结点
- size_t _n;
- };
- }
- //先改造哈希表
-
-
- //int main()
- //{
- //
- // /*txh::HashTable<int,int,> h1;*/
- //
- // //h1.insert(std::make_pair(4,1));
- // //h1.insert(std::make_pair(4,1));
- // //h1.insert(std::make_pair(4,1));
- // //h1.insert(std::make_pair(3,1));
- //
- // //h1.insert(std::make_pair(7,1));
- //
- // /*txh::HashTable<std::string, int,HashFunc<std::string>> h1;
- // h1.insert(std::make_pair("苹果", 1));
- // h1.insert(std::make_pair("香蕉", 1));
- // h1.insert(std::make_pair("梨", 1));
- // h1.insert(std::make_pair("苹果", 1));
- // txh::HashData<std::string, int>* ret = h1.find("苹果");
- // ret->_kv.second++;
- // h1.erase("苹果");*/
- //
- // HashBucket::HashTable<int, int, HashFunc<int>> h1;
- //
- // h1.insert(std::make_pair(3, 1));
- // h1.insert(std::make_pair(4, 1));
- // h1.insert(std::make_pair(3, 1));
- // h1.insert(std::make_pair(3, 1));
- // h1.insert(std::make_pair(12, 1));
- // h1.insert(std::make_pair(12, 1));
- // return 0;
- //}
- template<class T>
- struct compare
- {
- bool operator()(const T& t1,const T& t2)
- {
- return t1 > t2;
- }
- };

- #pragma once
- #include "HashTable.h"
- #include "unordered_map.h"
- #include "unordered_set.h"
- #include <iostream>
- namespace txh
- {
- template<class K,class T,class keyCon = HashFunc<K>>
- class unordered_map
- {
- public:
- struct mapkeyoft
- {
- const K& operator()(const std::pair<K,T>& kv)
- {
- return kv.first;
- }
- };
- typedef typename HashBucket::HashTable<K, std::pair<K, T>, keyCon, mapkeyoft>::iterator iterator;
- typedef typename HashBucket::HashTable<K, std::pair<K, T>, keyCon, 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();
- }
-
- std::pair<iterator, bool> insert(const std::pair<K, T>& data)
- {
- return _ht.insert(data);
- }
-
- T& operator[](const K& key)
- {
- std::pair<iterator, bool> ret = insert(std::make_pair(key
- , T));
- return ret.first->second;
- //加->就是得到了data,然后second
- }
-
- iterator find(const K& key)
- {
- return _ht.find(key);
- }
-
- bool erase(const K& key)
- {
- _ht.erase(key);
- }
- private:
- HashBucket::HashTable<K, std::pair<K, T>, keyCon, mapkeyoft> _ht;
-
- };
- }

- #pragma once
- #include "HashTable.h"
- #include <iostream>
- #include <utility>
- namespace txh
- {
- template<class K,class keyCon = HashFunc<K>>
- class unordered_set
- {
- struct keyofset
- {
- const K& operator()(const K& data)
- {
- return data;
- }
- };
-
- public:
- typedef typename HashBucket::HashTable<K, K, keyCon, keyofset>::const_iterator iterator;
- typedef typename HashBucket::HashTable<K, K, keyCon, keyofset>::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();
- }
-
- std::pair<iterator, bool> insert(const K& data)
- {
- return _ht.insert(data);
- }
-
- iterator find(const K& data)
- {
- return _ht.find(data);
- }
-
- bool erase(const K& data)
- {
- return _ht.erase(data);
- }
- private:
- HashBucket::HashTable<K, K, keyCon, keyofset> _ht;
- };
- }

test.cpp
- #define _CRT_SECURE_NO_WARNINGS 1
- #include "unordered_map.h"
- #include "unordered_set.h"
- #include "HashTable.h"
- #include <iostream>
- #include <algorithm>
- #include <utility>
- int main()
- {
- txh::unordered_set<int, HashFunc<int>> s1;
- s1.insert(1);
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。