当前位置:   article > 正文

模拟实现【哈希】_哈希仿函数

哈希仿函数

哈希

unordered系列关联式容器

unordered_set

和set接口和功能几乎一样。

unordered_map

1.unordered_map是储存<key,value>键值对的关联式容器,允许通过key快速索引与其对应的value。 2.在unordered_map中,键值用于唯一的标识元素,而映射值是一个对象,其内容与此键关联。键和映射值的类型可能不同。 3.在内部,unordered_map没有对<key,value>按照特定的舒徐排序,为了在常熟范围内找到key所对应的value,unordered_map将相同的键值放在了相同的桶中。 4.unordered_map容器通过key访问单个元素比map块,但它在遍历元素自己的范围迭代方面效率低。 5.unordered_map实现了直接访问操作符(operator[])。它允许key作为参数直接访问value。 6.它的迭代器至少是向前迭代器。

unordered_map接口

1.unordered_map构造

函数声明功能介绍
unordered_map构造不同格式的unordered_map对象

2.unordered_map容量

函数声明功能
bool empty()const检测为空
size_t size()const获取有效元素的个数

3.unordered_map迭代器

函数声明功能
begin返回第一个元素的迭代器
end返回最后一个元素的下一个位置的迭代器
cbeginconst begin()const
cendconst end()const

4.unordered_map的元素访问

函数声明功能
operator[]返回key对应的value,没有固定的默认值(取决于key)
*注意:该函数实际上调用哈希桶的插入操作,参数key与v()构造一个默认值往底层哈希桶插入
插入成功,返回v() 失败返回key对应的value的值*

5.unordered_map查询

函数声明功能
iterator find(const K& key)返回key在哈希桶中的位置
size_t count(const K& key)返回哈希桶中有关key的键值对的个数
unordered_map count返回的最大值为1,原因:不能重复

6.unordered_map的修改

函数声明功能
insert插入键值对
erase删除键值对
void clear()清空有效元素的个数
void swap(unordered_map&)交换两个容器中的元素(指针交换)

7.unordered_map操作

函数声明功能
size_t bucket count()const返回哈希桶中的总个数
size_t bucket size(size_t n)const返回n号桶有效元素的总个数
size_tbucket(const K& key)返回元素key所在的桶号

比较map和unordered_map的效率

  1. #include<iostream>
  2. #include<vector>
  3. #include<algorithm>
  4. #include<unordered_map>
  5. #include<map>
  6. #include<time.h>
  7. using namespace std;
  8. void test_map()
  9. {
  10. const int n = 100000;
  11. srand(time(0));
  12. vector<int> v;
  13. v.reserve(n);
  14. for (int i = 0; i < n; i++)
  15. {
  16. v.push_back(rand()+i);//伪随机只有3.5w左右
  17. }
  18. //测试插入的速度
  19. map<int, int> p;
  20. unordered_map<int, int> up;
  21. clock_t start1 = clock();
  22. for (auto e : v)
  23. {
  24. p.insert(make_pair(e, e));
  25. }
  26. clock_t end1 = clock();
  27. clock_t start2 = clock();
  28. for (auto e : v)
  29. {
  30. up.insert(make_pair(e, e));
  31. }
  32. clock_t end2 = clock();
  33. //查找速度
  34. clock_t start3 = clock();
  35. for (auto e : v)
  36. {
  37. p.find(e);
  38. }
  39. clock_t end3 = clock();
  40. clock_t start4 = clock();
  41. for (auto e : v)
  42. {
  43. up.find(e);
  44. }
  45. clock_t end4 = clock();
  46. //删除速度
  47. clock_t start5 = clock();
  48. for (auto e : v)
  49. {
  50. p.erase(e);
  51. }
  52. clock_t end5 = clock();
  53. clock_t start6 = clock();
  54. for (auto e : v)
  55. {
  56. up.erase(e);
  57. }
  58. clock_t end6 = clock();
  59. cout << "插入速度:" << endl << "map:" << end1 - start1 << endl << "unordered_map:" << end2 - start2 << endl;
  60. cout << "find速度:" << endl << "map:" << end3 - start3 << endl << "unordered_map:" << end4 - start4 << endl;
  61. cout << "erase速度:" << endl << "map:" << end5 - start5 << endl << "unordered_map:" << end6 - start6 << endl;
  62. }
  63. int main()
  64. {
  65. test_map();
  66. return 0;
  67. }

底层结构

unordered系列之所以效率比较高,是因为底层使用了哈希结构。

哈希概念

顺序结构及平衡树中,元素关键码与其存储位置间没有对应关系,因此,在查找一个元素时,必须多次经过关键码的比较。顺序查找的时间复杂度为O(N).平衡树中为树的高度,即O(logN),搜索的效率取决于搜索过程中元素的比较次数。

理想的搜索方法:可以不经过任何比较,一次直接从表中得到的要搜索的元素。 如果构造一种存储结构,通过某种函数(hashfunc)使元素的存储位置与他的该案件吗之间建立一一映射关系,那么在查找时通过该函数可以很快找到该元素。

该结构中:

插入元素 根据待插入元素的关键码,以此函数计算该元素的储存位置并按此位置进行存放。

存放元素 对元素的关键码进行同样的计算,把求得的函数值当作元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功。

该方式为哈希(散列)方法,哈希方法中使用的函数转化称为哈希(散列)函数,构造出来的结构称为哈希表(散列表)

int a[] = {1,7,4,6,5,9};
hashi(key)= key % capicity;

用这种方法,几乎不用做多次关键码比较,搜索速度特别快。 但是我要insert(17)?,会出现哈希冲突的问题。

哈希冲突

概念

不同关键字通过相同的哈希数计算相同的哈希地址,这种现象称为哈希冲突。 把具有相同哈希地址的数据元素称为”同义词“

哈希函数

能够触发哈希冲突,可能是哈希函数设计不够合理。 设计原则 1.哈希函数的定义域必须包括需要储存的全部关键码,而如果散列表允许有m个地址,其值域必须到[0,m-1]之间 2.哈希函数算出来的地址能均匀的分布在整个空间中。 3.哈希函数应该比较简单。

常见哈希函数 1.直接定址法 取关键字的某个线性函数为散列地址:Hash(key) = A*key+B 优点:简单,均匀 缺点:需要事先知道关键字的分布情况 使用场景:适合查找比较小且连续的情况

2.除留余数法 设散列表中允许的地址数为m,取一个不大于m,但接近或者等于m的质数作为除数,按照哈希函数:Hash(key) = key % p( p <= m ),将关键码换成哈希函数。

注意:哈希函数设计的越精妙,产生哈希冲突的可能性就越低,但是无法避免哈希冲突。

哈希冲突的解决

闭散列开散列

闭散列

闭散列:开放地址法,发生哈希冲突的时候,如果哈希表未装满,说明在哈希表中还有空位置,那么可以把key存放到冲突位置的”下一个“空位置中去。

线性探测 从发生冲突的位置开始,一次向后探测,直到寻找到下一个空位置为止。

思考: 哈希表啥时候可以进行扩容? 荷载因子: a = 填入表中的元素/散列表的长度 a在[0.7,0.8]为佳。

线性探测特性
优点实现非常简单
缺点一旦发生哈希冲突,却数据量较大,容易引发”堵塞“,导致搜索效率降低

二次探测 为避免数据过于集中导致的”堵塞“,使用二次探测可以避免该问题。 在线性探测中i = 1,在二次探测中i的值可以随机

研究表明:当表的长度为质数且表装载因子a不超过0.5时,新的表项一定能够插入,而且任何一个位置都不会被探查两次,因此只要表中有一半的空位置,既不会存在表装满的问题。在搜索时可以不考虑表装满的情况,但插入时必须保证a<=0.5

开散列的最大缺陷就是空间利用率比较低。这也是哈希缺陷 相关代码: 初版,能用但不是目前最优秀的表

#include<iostream>
#include<vector>
#include<string>
using namespace std;
​
enum State
{
    EMPTY,
    EXSIST,
    DELETE
};
template< class K,class V>
struct HashData
{
    pair<K, V> _kv;
    State _state = EMPTY;//获取某个桶的状态,给个缺省值。
    
};
//仿函数,完成对内置类型和string的支持
template<class K>
struct DefaultHash
{
    size_t operator()(const K& key)
    {
        return (size_t)key;
    }
};
​
//模板特化,支持string
template<>
struct DefaultHash<string>
{
    //使用BKDR算法
    size_t operator()(const string& key)
    {
        size_t hash = 0;
        for (auto ch : key)
        {
            hash = hash * 131 + ch;
        }
        return hash;
    }
};
​
template<class K,class V,class Hashfunc = DefaultHash<K>>
class HashTable
{
    typedef HashData<K, V> Data;
public:
    bool insert(const pair<K, V>& kv)
    {
        //hash的扩容必须处理,一般情况下,hash是绝对不会被存满的。
        //就像二叉搜索树一样,欸一个关键字因子
        //
        Hashfunc hf;
        //去重
        if (find(kv.first))//存在就不要再插入了。
            return false;
        //首先判读size()是不是为0?
        //判断关键字因子是不是超过了70%
        if (_table.size() == 0 || _n * 10 / _table.size() >= 7)//这里次序不能颠倒,否则会报错
        {
            size_t newsize = _table.size() == 0 ? 10 : _table.size() * 2;
            //一旦更新,个元素相对于hashtable的位置就会出错,需要单独调整
            //现代写法:通过船舰一个新的hashtable,通过调用insert函数,此时不会走if语句
            //编译器跑完以后,交换指针即可。
            HashTable<K, V> hashtable;
            hashtable._table.resize(newsize);
            //
            for (auto& e : _table)
            {
                if(e._state == EXSIST)
                    hashtable.insert(e._kv);
            }
            hashtable._table.swap(_table);//交换指针,因为hashtable是临时对象,生命周期结束后,会自己析构。
        }
​
        size_t starti = hf(kv.first);//获取key
        //将key%=size()获取映射在hashtable中的位置
        starti %= _table.size();
        size_t hashi = starti;
​
        //二次查找、扫描
        int i = 1;//采取的是闭散列法,挪到下一个位置上
        while (_table[hashi]._state == EXSIST)//当桶的状态为EXIST时,不能存放数据没。
        {
            hashi = hashi + i;
        }
​
        //赋值
        _table[hashi]._kv = kv;
        //更改状态
        _table[hashi]._state = EXSIST;
        _n++;
        return true;
    }
    Data* find(const K& key)//找到返回指针,找不到返回nullptr
    {
        if (_table.size() == 0)
        {
            return nullptr;
        }
        //为空的时候就停止,empty和delete继续
        //闭散列的存储方法,所找的数据都应该在[hashi,_table.end()]内,为空就结束了
        Hashfunc hf;
        size_t starti = hf(key);
        starti %= _table.size();
        size_t hashi = starti;
​
        int i = 1;
        while (_table[hashi]._state != EMPTY)
        {
            if (_table[hashi]._state == EXSIST && hf(_table[hashi]._kv.first) == hf(key))
                //删除是伪删除法,本质是数据的覆盖,所以要state 和 值双重排定
            {
                return &_table[hashi];
            }
            hashi = hashi + i;
            hashi %= _table.size();//防止hashi越界。
        }
        return nullptr;
    }
​
    bool erase(const K& key)
    {
        //为空,就不要删除了
        if (_table.size() == 0)
            return false;
        //找到元素
        Data* ret = find(key);
​
        if (ret)//存在
        {
            ret->_state = DELETE;
            _n--;//关键字因子个数要处理,因为exist变化了。
            return true;
        }
        else
        {
            return false;
        }
    }
private:
    vector<Data> _table;
    size_t _n = 0; //关键字因子的个数
};

开散列

概念

开散列法又叫做链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址空间的关键码归于同一自己和,每个子集合成为一个桶,各个桶中的元素通过一个单链表连起来,各链表的头节点储存在哈希表中。

开散列增容:

桶数量是一定数量的,随着新元素的插入,每个桶中的元素不断增多,极端情况下,可能导致一个同种链表节点非常多,会影响哈希表的性能,所以条件合理的情况下,要对哈希表进行扩容。

扩容条件:每个哈希桶中刚好挂一个节点,继续插入元素时,每一次都会发生哈希冲突。因此,在元素个数刚好等于桶的个数时,可以进行扩容。

相关代码:

  1. #include<iostream>
  2. #include<vector>
  3. #include<string>
  4. using namespace std;
  5. enum State
  6. {
  7. EMPTY,
  8. EXSIST,
  9. DELETE
  10. };
  11. template< class K,class V>
  12. struct HashData
  13. {
  14. pair<K, V> _kv;
  15. State _state = EMPTY;//获取某个桶的状态,给个缺省值。
  16. };
  17. //仿函数,完成对内置类型和string的支持
  18. template<class K>
  19. struct DefaultHash
  20. {
  21. size_t operator()(const K& key)
  22. {
  23. return (size_t)key;
  24. }
  25. };
  26. //模板特化,支持string
  27. template<>
  28. struct DefaultHash<string>
  29. {
  30. //使用BKDR算法
  31. size_t operator()(const string& key)
  32. {
  33. size_t hash = 0;
  34. for (auto ch : key)
  35. {
  36. hash = hash * 131 + ch;
  37. }
  38. return hash;
  39. }
  40. };
  41. template<class K,class V,class Hashfunc = DefaultHash<K>>
  42. class HashTable
  43. {
  44. typedef HashData<K, V> Data;
  45. public:
  46. bool insert(const pair<K, V>& kv)
  47. {
  48. //hash的扩容必须处理,一般情况下,hash是绝对不会被存满的。
  49. //就像二叉搜索树一样,欸一个关键字因子
  50. //
  51. Hashfunc hf;
  52. //去重
  53. if (find(kv.first))//存在就不要再插入了。
  54. return false;
  55. //首先判读size()是不是为0?
  56. //判断关键字因子是不是超过了70%
  57. if (_table.size() == 0 || _n * 10 / _table.size() >= 7)//这里次序不能颠倒,否则会报错
  58. {
  59. size_t newsize = _table.size() == 0 ? 10 : _table.size() * 2;
  60. //一旦更新,个元素相对于hashtable的位置就会出错,需要单独调整
  61. //现代写法:通过船舰一个新的hashtable,通过调用insert函数,此时不会走if语句
  62. //编译器跑完以后,交换指针即可。
  63. HashTable<K, V> hashtable;
  64. hashtable._table.resize(newsize);
  65. //
  66. for (auto& e : _table)
  67. {
  68. if(e._state == EXSIST)
  69. hashtable.insert(e._kv);
  70. }
  71. hashtable._table.swap(_table);//交换指针,因为hashtable是临时对象,生命周期结束后,会自己析构。
  72. }
  73. size_t starti = hf(kv.first);//获取key
  74. //将key%=size()获取映射在hashtable中的位置
  75. starti %= _table.size();
  76. size_t hashi = starti;
  77. //二次查找、扫描
  78. int i = 1;//采取的是闭散列法,挪到下一个位置上
  79. while (_table[hashi]._state == EXSIST)//当桶的状态为EXIST时,不能存放数据没。
  80. {
  81. hashi = hashi + i;
  82. }
  83. //赋值
  84. _table[hashi]._kv = kv;
  85. //更改状态
  86. _table[hashi]._state = EXSIST;
  87. _n++;
  88. return true;
  89. }
  90. Data* find(const K& key)//找到返回指针,找不到返回nullptr
  91. {
  92. if (_table.size() == 0)
  93. {
  94. return nullptr;
  95. }
  96. //为空的时候就停止,empty和delete继续
  97. //闭散列的存储方法,所找的数据都应该在[hashi,_table.end()]内,为空就结束了
  98. Hashfunc hf;
  99. size_t starti = hf(key);
  100. starti %= _table.size();
  101. size_t hashi = starti;
  102. int i = 1;
  103. while (_table[hashi]._state != EMPTY)
  104. {
  105. if (_table[hashi]._state == EXSIST && hf(_table[hashi]._kv.first) == hf(key))
  106. //删除是伪删除法,本质是数据的覆盖,所以要state 和 值双重排定
  107. {
  108. return &_table[hashi];
  109. }
  110. hashi = hashi + i;
  111. hashi %= _table.size();//防止hashi越界。
  112. }
  113. return nullptr;
  114. }
  115. bool erase(const K& key)
  116. {
  117. //为空,就不要删除了
  118. if (_table.size() == 0)
  119. return false;
  120. //找到元素
  121. Data* ret = find(key);
  122. if (ret)//存在
  123. {
  124. ret->_state = DELETE;
  125. _n--;//关键字因子个数要处理,因为exist变化了。
  126. return true;
  127. }
  128. else
  129. {
  130. return false;
  131. }
  132. }
  133. private:
  134. vector<Data> _table;
  135. size_t _n = 0; //关键字因子的个数
  136. };
  137. namespace Bucket
  138. {
  139. template<class K,class V>
  140. struct HashNode
  141. {
  142. pair<K, V> _kv;
  143. HashNode<K, V>* _next;
  144. //构造函数
  145. HashNode(const pair<K, V>& kv)
  146. :_kv(kv)
  147. ,_next(nullptr)
  148. {}
  149. };
  150. template<class K>
  151. struct DefaultHash
  152. {
  153. size_t operator()(const K& key)
  154. {
  155. return (size_t)key;
  156. }
  157. };
  158. template<>
  159. struct DefaultHash<string>
  160. {
  161. size_t operator()(const string& key)
  162. {
  163. size_t hash = 0;
  164. for (auto ch : key)
  165. {
  166. hash += ch * 131;
  167. }
  168. return hash;
  169. }
  170. };
  171. template<class K,class V>
  172. class HashTable
  173. {
  174. typedef HashNode<K, V> Node;
  175. public:
  176. ~HashTable()
  177. {
  178. for (int i = 0; i < _table.size(); i++)
  179. {
  180. Node* cur = _table[i];
  181. while (cur)
  182. {
  183. Node* next = cur->_next;
  184. delete cur;
  185. cur = next;
  186. }
  187. }
  188. }
  189. bool insert(const pair<K, V>& kv)
  190. {
  191. if (find(kv.first))
  192. return false;
  193. //扩容否?
  194. if (_n == _table.size())
  195. {
  196. size_t newSize = _table.size() == 0 ? 10 : _table.size() * 2;
  197. //重新映射
  198. HashTable<K, V> newHT;
  199. newHT.resize(newSize,nullptr);
  200. for (int i = 0; i < _table.size(); i++)
  201. {
  202. Node* cur = _table[i];
  203. while (cur)
  204. {
  205. newHT.insert(cur->_kv);
  206. cur = cur->_next;
  207. }
  208. }
  209. newHT._table.swap(_table);//需要析构函数
  210. //原因:vector自己调用析构函数,但是Node节点却不会自己析构,因为其是内置类型的。
  211. }
  212. size_t starti = kv.first;
  213. starti %= _table.size();
  214. size_t hashi = starti;
  215. Node* newnode = new Node(kv);
  216. //头插法链接
  217. newnode->_next = _table[hashi];
  218. _table[hashi] = newnode;
  219. _n++;
  220. }
  221. Node* find(const K& key)
  222. {
  223. if (_table.size() == 0)
  224. {
  225. return nullptr;
  226. }
  227. size_t starti = key;
  228. size_t hashi = starti %= _table.size();
  229. Node* cur = _table[hashi];//找到key所在的哪个桶
  230. while (cur)
  231. {
  232. if (cur->_kv.first == key)
  233. {
  234. return true;
  235. }
  236. cur = cur->_next;
  237. }
  238. return false;
  239. }
  240. bool erase(const K& key)
  241. {
  242. if (_table.size() == 0)
  243. return false;
  244. size_t starti = key;
  245. size_t hashi = starti %= _table.size();
  246. Node* cur = _table[hashi];
  247. Node* prev = nullptr;
  248. while (cur)
  249. {
  250. if (prev == nullptr)//头节点
  251. {
  252. _table[hashi] = cur->_next;
  253. delete cur;
  254. return true;
  255. }
  256. else//非头结点
  257. {
  258. prev->_next = cur->_next;
  259. delete cur;
  260. return true;
  261. }
  262. //更新
  263. prev = cur;
  264. cur = cur->_next;
  265. }
  266. return false;
  267. }
  268. private:
  269. vector<Node*> _table;
  270. size_t _n = 0;
  271. };
  272. }

哈希的模拟实现

类似于map和set的封装 template<class K,class T,class KeyOfT(获取key),class hashFunc(获取hashi,映射)>

hash.h

  1. #pragma once
  2. #include<iostream>
  3. #include<vector>
  4. #include<string>
  5. using namespace std;
  6. template<class K>
  7. struct DefaultHash
  8. {
  9. size_t operator()(const K& key)
  10. {
  11. return (size_t)key;
  12. }
  13. };
  14. template<>
  15. struct DefaultHash<string>
  16. {
  17. size_t operator()(const string& key)
  18. {
  19. size_t hash = 0;
  20. for (auto ch : key)
  21. {
  22. hash = hash*131 + ch;
  23. }
  24. return hash;
  25. }
  26. };
  27. namespace Bucket
  28. {
  29. template<class T>
  30. struct HashNode
  31. {
  32. T _data;
  33. HashNode<T>* _next;
  34. //构造函数
  35. HashNode(const T& data)
  36. :_data(data)
  37. , _next(nullptr)
  38. {}
  39. };
  40. //set
  41. //template<class K,class K,class KeyOfT,class hashfunc>
  42. /*map
  43. template<class K,class V,class KeyOfT,class hashFunc>*/
  44. //必须前置声明HashTable
  45. template<class K, class T, class KeyOfT, class hashFunc>
  46. class HashTable;
  47. template<class K, class T, class KeyOfT, class hashFunc>
  48. class __HTIterator
  49. {
  50. typedef HashNode<T> Node;
  51. typedef __HTIterator<K, T, KeyOfT, hashFunc> Self;
  52. public:
  53. Node* _node;//迭代器返回的是节点
  54. HashTable<K, T, KeyOfT, hashFunc>* _pht;//表的指针
  55. //进行迭代器的处理
  56. __HTIterator(Node* node, HashTable<K, T, KeyOfT, hashFunc>* pht)
  57. :_node(node)
  58. , _pht(pht)
  59. {}
  60. //前置++
  61. Self& operator++()
  62. {
  63. if (_node->_next)//下一个不为空
  64. {
  65. _node = _node->_next;
  66. }
  67. else//为空
  68. {
  69. //确定_node的位置
  70. KeyOfT kot;
  71. hashFunc hf;
  72. size_t hashi = hf(kot(_node->_data));//转成key->hashi
  73. hashi %= _pht->_table.size();//找到映射位置
  74. ++hashi;
  75. for (; hashi < _pht->_table.size(); hashi++)
  76. {
  77. if (_pht->_table[hashi])//找到
  78. {
  79. _node = _pht->_table[hashi];
  80. break;
  81. }
  82. }
  83. if (hashi == _pht->_table.size())//没找到
  84. {
  85. _node = nullptr;
  86. }
  87. }
  88. return *this;
  89. }
  90. Self operator++(int)
  91. {
  92. Self tmp(*this);
  93. ++(this);
  94. return tmp;
  95. }
  96. T& operator*()
  97. {
  98. return _node->_data;
  99. }
  100. T* operator->()
  101. {
  102. return &_node->_data;
  103. }
  104. bool operator==(const Self& s)const
  105. {
  106. return _node == s._node;
  107. }
  108. bool operator!=(const Self& s)const
  109. {
  110. return _node != s._node;
  111. }
  112. };
  113. template<class K, class T, class KeyOfT, class hashFunc>
  114. class HashTable
  115. {
  116. template<class K, class T, class KeyOfT, class hashFunc>
  117. friend class __HTIterator;
  118. typedef HashNode<T> Node;
  119. public:
  120. typedef __HTIterator<K, T, KeyOfT, hashFunc> iterator;
  121. //iterator 有两个参数 _node _pht
  122. iterator begin()
  123. {
  124. //找到第一个元素
  125. for (size_t i = 0; i < _table.size(); i++)
  126. {
  127. if (_table[i])
  128. {
  129. return iterator(_table[i], this);//单参数才支持隐士类型的转化
  130. }
  131. }
  132. //找不到
  133. return end();
  134. }
  135. iterator end()
  136. {
  137. return iterator(nullptr, this);//单参数才支持隐士类型的转化
  138. }
  139. public:
  140. ~HashTable()
  141. {
  142. size_t i = 0;
  143. for (; i < _table.size(); i++)
  144. {
  145. Node* cur = _table[i];
  146. while (cur)
  147. {
  148. Node* next = cur->_next;
  149. delete cur;
  150. cur = next;
  151. }
  152. _table[i] = nullptr;
  153. }
  154. }
  155. bool Insert(const T& data)
  156. {
  157. KeyOfT kot;//用于获取key的值
  158. hashFunc hf;//哈希函数,用于获取对应的hash
  159. if (Find(kot(data)))
  160. return false;
  161. //扩容否?
  162. if (_n == _table.size())
  163. {
  164. size_t newSize = _table.size() == 0 ? 10 : _table.size() * 2;
  165. //重新映射
  166. /*HashTable<K, V> newHT;
  167. newHT.resize(newSize,nullptr);*/
  168. vector<Node*> newHT;
  169. newHT.resize(newSize, nullptr);
  170. for (size_t i = 0; i < _table.size(); i++)
  171. {
  172. Node* cur = _table[i];
  173. while (cur)
  174. {
  175. /*newHT.Insert(cur->_kv);
  176. cur = cur->_next;*/
  177. //优化写法,获取原表的节点,直接链接到newHT
  178. Node* next = cur->_next;//手动记录下一个cur的下一个节点
  179. size_t starti = hf(kot(cur->_data));//获取到key以后,再获取key的映射hash
  180. size_t hashi = starti %= newSize;//获取映射的hashi
  181. //链接,头插法。
  182. cur->_next = newHT[hashi];
  183. newHT[hashi] = cur;
  184. cur = next;
  185. }
  186. _table[i] = nullptr;
  187. }
  188. newHT.swap(_table);//需要析构函数
  189. //原因:vector自己调用析构函数,但是Node节点却不会自己析构,因为其是内置类型的。
  190. }
  191. size_t starti = kot(data);
  192. starti %= _table.size();
  193. size_t hashi = hf(starti);
  194. Node* newnode = new Node(data);
  195. //头插法链接
  196. newnode->_next = _table[hashi];
  197. _table[hashi] = newnode;
  198. _n++;
  199. return true;
  200. }
  201. Node* Find(const K& key)
  202. {
  203. hashFunc hf;
  204. KeyOfT kot;
  205. if (_table.size() == 0)
  206. {
  207. return nullptr;
  208. }
  209. size_t starti = hf(key);
  210. size_t hashi = starti %= _table.size();
  211. Node* cur = _table[hashi];//找到key所在的哪个桶
  212. while (cur)
  213. {
  214. if (kot(cur->_data) == key)
  215. {
  216. return cur;
  217. }
  218. cur = cur->_next;
  219. }
  220. return nullptr;
  221. }
  222. bool Erase(const K& key)
  223. {
  224. hashFunc hf;
  225. if (_table.size() == 0)
  226. return false;
  227. size_t starti = hf(key);//获取hash,传递的是否类型是不知道的
  228. size_t hashi = starti %= _table.size();
  229. Node* cur = _table[hashi];
  230. Node* prev = nullptr;
  231. while (cur)
  232. {
  233. if (prev == nullptr)//头节点
  234. {
  235. _table[hashi] = cur->_next;
  236. delete cur;
  237. return true;
  238. }
  239. else//非头结点
  240. {
  241. prev->_next = cur->_next;
  242. delete cur;
  243. return true;
  244. }
  245. //更新
  246. prev = cur;
  247. cur = cur->_next;
  248. }
  249. return false;
  250. }
  251. private:
  252. vector<Node*> _table;
  253. size_t _n = 0;
  254. };
  255. }
 

unordered_set

  1. #include"Hash.h"
  2. namespace bit
  3. {
  4. template<class K,class hashFunc = DefaultHash<K>>
  5. class set
  6. {
  7. struct SetKeyOfT
  8. {
  9. const K& operator()(const K& key)
  10. {
  11. return key;
  12. }
  13. };
  14. public:
  15. typedef typename Bucket::HashTable<K, K, SetKeyOfT, hashFunc>::iterator iterator;//去类里面的内嵌类型,需要typename
  16. iterator begin()
  17. {
  18. return _ht.begin();
  19. }
  20. iterator end()
  21. {
  22. return _ht.end();
  23. }
  24. bool insert(const K& key)
  25. {
  26. return _ht.Insert(key);
  27. }
  28. private:
  29. Bucket::HashTable<K, K,SetKeyOfT,hashFunc> _ht;
  30. };
  31. }

unordered_map

  1. #include"Hash.h"
  2. namespace bit
  3. {
  4. template<class K,class V,class hashFunc = DefaultHash<K>>
  5. class unordered_map
  6. {
  7. struct MapKeyOfT
  8. {
  9. const K& operator()(const pair<K, V>& kv)
  10. {
  11. return kv.first;
  12. }
  13. };
  14. public:
  15. typedef typename Bucket::HashTable<K, pair<K, V>, MapKeyOfT, hashFunc>::iterator iterator;
  16. iterator begin()
  17. {
  18. return _ht.begin();
  19. }
  20. iterator end()
  21. {
  22. _ht.end();
  23. }
  24. bool insert(const pair<K,V>& kv)
  25. {
  26. _ht.Insert(kv);
  27. }
  28. private:
  29. Bucket::HashTable<K, pair<K,V>, MapKeyOfT> _ht;
  30. };
  31. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/941276
推荐阅读
相关标签
  

闽ICP备14008679号