• C++---哈希


    目录

    1. unordered系列关联式容器

    1.1 unordered_map

    1.1.1 unordered_map的介绍

    1.1.2 unordered_map的接口说明

    1.2 unordered_set

    2. 底层结构

    2.1 哈希概念

    2.2 哈希冲突

    2.3 哈希函数

    2.4 哈希冲突解决

    2.4.1 闭散列

    2.4.2 开散列

    3. 封装unorder_map和unorder_set

    3.1 unorder_map 的封装

    3.2 unorder_set 的封装

    3.4 unordered_map 加迭代器的封装

    3.5 unordered_set 加迭代器的封装

    1. unordered系列关联式容器

    1.1 unordered_map

    1.1.1 unordered_map的介绍

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

    1.1.2 unordered_map的接口说明

    1. unordered_map的构造

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

    2. unordered_map的容量

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

    3. unordered_map的迭代器

    函数声明功能介绍
    begin返回unordered_map第一个元素的迭代器
    end返回unordered_map最后一个元素下一个位置的迭代器
    cbegin返回unordered_map第一个元素的const迭代器
    cend返回unordered_map最后一个元素下一个位置的const迭代器

    4. unordered_map的元素访问

    函数声明功能介绍
    operator[]返回与key对应的value,没有一个默认值

    5. unordered_map的查询

    函数声明功能介绍
    iterator find(const K& key)返回key在哈希桶中的位置
    size_t count(const K& key)返回哈希桶中关键码为key的键值对的个数

    注意:unordered_map中key是不能重复的,因此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_t bucket(const K& key)返回元素key所在的桶号

    1.2 unordered_set

    与unordered_map类似,详情见文档unordered_set

    2. 底层结构

    unordered系列的关联式容器之所以效率比较高,是因为其底层使用了哈希结构

    2.1 哈希概念

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

            理想的搜索方法可以不经过任何比较,一次直接从表中得到要搜索的元素

            如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。

    当向该结构中:

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

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

    该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称
    为哈希表(Hash Table)(或者称散列表)。

    例如:数据集合{1,7,6,4,5,9};
    哈希函数设置为:hash(key) = key % capacity ; capacity为存储元素底层空间总的大小。

    问题:如果继续向上述哈希表中插入44,会出现什么问题?

    2.2 哈希冲突

            上述问题中,不同关键字通过相同哈希函数计算出相同的哈希地址,该种现象称为哈希冲突
    哈希碰撞

            把具有不同关键码而具有相同哈希地址的数据元素称为“同义词”。

            发生哈希冲突该如何处理呢?

    2.3 哈希函数

    引起哈希冲突的一个原因可能是:哈希函数设计不够合理

    哈希函数设计原则:
            哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值
    域必须在0到m-1之间
            哈希函数计算出来的地址能均匀分布在整个空间中
            哈希函数应该比较简单

    常见哈希函数:

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

    例题:字符串中的第一个唯一字符

    1. class Solution {
    2. public:
    3. int firstUniqChar(string s) {
    4. int hash[256] = {0};
    5. for(int i = 0; i < s.size(); i++){
    6. hash[s[i]]++;
    7. }
    8. for(int i = 0; i < s.size(); i++){
    9. if(hash[s[i]] == 1){
    10. return i;
    11. }
    12. }
    13. return -1;
    14. }
    15. };

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

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

    2.4 哈希冲突解决

    解决哈希冲突两种常见的方法是闭散列开散列

    2.4.1 闭散列

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

    那如何寻找下一个空位置呢?

    1. 线性探测

            比如2.1中的场景,现在需要插入元素44,先通过哈希函数计算哈希地址,hashAddr为4,
    因此44理论上应该插在该位置,但是该位置已经放了值为4的元素,即发生哈希冲突。

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

    插入:
            通过哈希函数获取待插入元素在哈希表中的位置
            如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突,使用线性探测找到下一个空位置,插入新元素。

    删除:
            采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素
    会影响其他元素的搜索
    。比如删除元素4,如果直接删除掉,44查找起来可能会受影响。因此线性探测采用标记的伪删除法来删除一个元素。

    1. // 哈希表每个空间给个标记
    2. // EMPTY此位置空, EXIST此位置已经有元素, DELETE元素已经删除
    3. enum State
    4. {EMPTY, EXIST, DELETE};

    载荷因子:

            为了解决 “哈希表什么情况下进行扩容?如何扩容?” 的问题,我们引入载荷因子的概念。

            载荷因子 = 表中元素个数 / 散列表容量

            载荷因子越大,表中元素越多,产生冲突可能性越大;

            载荷因子越小,表中元素越少,产生冲突可能性越小;

            当达到载荷因子我们就扩容,如果载荷因子设置的太小,空间资源浪费就多,载荷因子设置越大,产生冲突的可能性就越大。因此,一般设置载荷因子为 0.7——0.8。

    线性探测简要实现哈希表代码如下:

    1. enum State//标志位
    2. {
    3. EMPTY,
    4. EXIST,
    5. DELETE
    6. };
    7. template<class K,class V>
    8. struct HashData//节点元素包含键值对和标志位
    9. {
    10. pair _kv;
    11. State _state = EMPTY;
    12. };
    13. template<class K>
    14. struct HashFunc//用于方便比较的仿函数
    15. {
    16. size_t operator()(const K& key)
    17. {
    18. return (size_t)key;
    19. }
    20. };
    21. //特化
    22. template<>
    23. struct HashFunc
    24. {
    25. size_t operator()(const string& key)
    26. {
    27. size_t val = 0;
    28. for (auto ch : key)
    29. {
    30. //防止出现abc = cba 这种情况,有大佬提出加每个字符前*131
    31. val *= 131;
    32. val += ch;
    33. }
    34. return val;
    35. }
    36. };
    37. //因为如果K为string类型,无法用 % 来实现映射关系,因此我们写个Hash仿函数
    38. template<class K, class V, class Hash = HashFunc>
    39. class HashTable
    40. {
    41. public:
    42. bool Insert(const pair& kv)
    43. {
    44. if (Find(kv.first))
    45. return false;
    46. //表为空或载荷因子超70%就扩容
    47. if (_tables.size() == 0 || 10 * _size / _tables.size() >= 7)
    48. {
    49. size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
    50. HashTable newHT;
    51. newHT._tables.resize(newSize);
    52. //旧表数据映射到新表
    53. for (auto e : _tables)
    54. {
    55. if (e._state == EXIST)
    56. {
    57. newHT.Insert(e._kv);
    58. }
    59. }
    60. _tables.swap(newHT._tables);
    61. }
    62. Hash hash;
    63. size_t hashi = hash(kv.first) % _tables.size();
    64. //线性探测
    65. while (_tables[hashi]._state == EXIST)
    66. {
    67. hashi++;
    68. hashi %= _tables.size();
    69. }
    70. _tables[hashi]._kv = kv;
    71. _tables[hashi]._state = EXIST;
    72. ++_size;
    73. return true;
    74. }
    75. HashData* Find(const K& key)
    76. {
    77. if (_tables.size() == 0)
    78. {
    79. return nullptr;
    80. }
    81. Hash hash;
    82. size_t hashi = hash(key) % _tables.size();
    83. while (_tables[hashi]._state != EMPTY)
    84. {
    85. if (_tables[hashi]._state != DELETE && _tables[hashi]._kv.first == key)
    86. {
    87. return &_tables[hashi];
    88. }
    89. hashi++;
    90. hashi %= _tables.size();
    91. }
    92. return nullptr;
    93. }
    94. bool Erase(const K& key)
    95. {
    96. HashData* ret = Find(key);
    97. if (ret)
    98. {
    99. ret->_state = DELETE;
    100. --_size;
    101. return true;
    102. }
    103. else
    104. {
    105. return false;
    106. }
    107. }
    108. private:
    109. vector> _tables;
    110. size_t _size = 0;//存储多少有效数据
    111. };

    总结

            线性探测优点:实现非常简单
            线性探测缺点:某个位置冲突很多的情况下,互相占用,冲突一片,如下图:

    2. 二次探测

    二次探测不是探测二次,是探测2的 i 次方:

     二次探测只需要把原来线性探测略微改动一下即可:

    1. bool Insert(const pair& kv)
    2. {
    3. if (Find(kv.first))
    4. return false;
    5. //表为空或载荷因子超70%就扩容
    6. if (_tables.size() == 0 || 10 * _size / _tables.size() >= 7)
    7. {
    8. size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
    9. HashTable newHT;
    10. newHT._tables.resize(newSize);
    11. //旧表数据映射到新表
    12. for (auto e : _tables)
    13. {
    14. if (e._state == EXIST)
    15. {
    16. newHT.Insert(e._kv);
    17. }
    18. }
    19. _tables.swap(newHT._tables);
    20. }
    21. Hash hash;
    22. size_t start = hash(kv.first) % _tables.size();
    23. size_t i = 0;
    24. size_t hashi = start;
    25. //二次探测
    26. while (_tables[hashi]._state == EXIST)
    27. {
    28. ++i;
    29. hashi = start + i * i;
    30. hashi %= _tables.size();
    31. }
    32. _tables[hashi]._kv = kv;
    33. _tables[hashi]._state = EXIST;
    34. ++_size;
    35. return true;
    36. }

    2.4.2 开散列

    开散列概念

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

    哈希桶简单实现:

    1. template<class K>
    2. struct HashFunc
    3. {
    4. size_t operator()(const K& key)
    5. {
    6. return (size_t)key;
    7. }
    8. };
    9. //特化
    10. template<>
    11. struct HashFunc
    12. {
    13. size_t operator()(const string& key)
    14. {
    15. size_t val = 0;
    16. for (auto ch : key)
    17. {
    18. //防止出现abc = cba 这种情况,有大佬提出加每个字符前*131
    19. val *= 131;
    20. val += ch;
    21. }
    22. return val;
    23. }
    24. };
    25. namespace HashBucket
    26. {
    27. template<class K, class V>
    28. struct HashNode
    29. {
    30. pair _kv;
    31. HashNode* _next;
    32. HashNode(const pair& kv)
    33. :_kv(kv)
    34. ,_next(nullptr)
    35. {}
    36. };
    37. template<class K,class V, class Hash = HashFunc>
    38. class HashTable
    39. {
    40. typedef HashNode Node;
    41. public:
    42. ~HashTable()
    43. {
    44. for (size_t i = 0; i < _tables.size(); ++i)
    45. {
    46. Node* cur = _tables[i];
    47. while (cur)
    48. {
    49. Node* next = cur->_next;
    50. delete cur;
    51. cur = next;
    52. }
    53. _tables[i] = nullptr;
    54. }
    55. }
    56. bool Insert(const pair& kv)
    57. {
    58. //去重
    59. if (Find(kv.first))
    60. {
    61. return false;
    62. }
    63. Hash hash;
    64. //负载因子到1就扩容
    65. if (_size == _tables.size())
    66. {
    67. size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
    68. vector newTables;
    69. newTables.resize(newSize, nullptr);
    70. //旧表中节点移动映射到新表
    71. for (size_t i = 0; i < _tables.size(); ++i)
    72. {
    73. Node* cur = _tables[i];
    74. while (cur)
    75. {
    76. Node* next = cur->_next;
    77. size_t hashi = hash(cur->_kv.first) % newTables.size();
    78. cur->_next = newTables[hashi];
    79. newTables[hashi] = cur;
    80. cur = next;
    81. }
    82. _tables[i] = nullptr;
    83. }
    84. _tables.swap(newTables);
    85. }
    86. size_t hashi = hash(kv.first) % _tables.size();
    87. //头插
    88. Node* newnode = new Node(kv);
    89. newnode->_next = _tables[hashi];
    90. _tables[hashi] = newnode;
    91. ++_size;
    92. return true;
    93. }
    94. Node* Find(const K& key)
    95. {
    96. if (_tables.size() == 0)
    97. {
    98. return nullptr;
    99. }
    100. Hash hash;
    101. size_t hashi = hash(key) % _tables.size();
    102. Node* cur = _tables[hashi];
    103. while (cur)
    104. {
    105. if (cur->_kv.first == key)
    106. {
    107. return cur;
    108. }
    109. cur = cur->_next;
    110. }
    111. return nullptr;
    112. }
    113. bool Erase(const K& key)
    114. {
    115. if (_tables.size() == 0)
    116. {
    117. return false;
    118. }
    119. Hash hash;
    120. size_t hashi = hash(key) % _tables.size();
    121. Node* prev = nullptr;
    122. Node* cur = _tables[hashi];
    123. while (cur)
    124. {
    125. if (cur->_kv.first == key)
    126. {
    127. // 1、头删
    128. // 2、中间删
    129. if (prev == nullptr)
    130. {
    131. _tables[hashi] = cur->_next;
    132. }
    133. else
    134. {
    135. prev->_next = cur->_next;
    136. }
    137. delete cur;
    138. --_size;
    139. return true;
    140. }
    141. prev = cur;
    142. cur = cur->_next;
    143. }
    144. return false;
    145. }
    146. size_t Size()
    147. {
    148. return _size;
    149. }
    150. // 表的长度
    151. size_t TablesSize()
    152. {
    153. return _tables.size();
    154. }
    155. // 桶的个数
    156. size_t BucketNum()
    157. {
    158. size_t num = 0;
    159. for (size_t i = 0; i < _tables.size(); ++i)
    160. {
    161. if (_tables[i])
    162. {
    163. ++num;
    164. }
    165. }
    166. return num;
    167. }
    168. size_t MaxBucketLenth()
    169. {
    170. size_t maxLen = 0;
    171. for (size_t i = 0; i < _tables.size(); ++i)
    172. {
    173. size_t len = 0;
    174. Node* cur = _tables[i];
    175. while (cur)
    176. {
    177. ++len;
    178. cur = cur->_next;
    179. }
    180. //if (len > 0)
    181. //printf("[%d]号桶长度:%d\n", i, len);
    182. if (len > maxLen)
    183. {
    184. maxLen = len;
    185. }
    186. }
    187. return maxLen;
    188. }
    189. private:
    190. vector _tables;
    191. size_t _size = 0;//存储有效数据个数
    192. };
    193. }

    3. 封装unorder_map和unorder_set

    简单修改上述哈希桶用于封装实现unorder_map和unorder_set.

    首先,我们将原来节点中用来存储节点值的 pair键值对 改为 模板参数 T.

    1. // 因为关联式容器中存储的是的键值对,因此
    2. // k为key的类型,
    3. // T: 如果是map,则为pair; 如果是set,则为k
    4. // KeyOfT: 通过T来获取key的一个仿函数类
    5. namespace Bucket
    6. {
    7. template<class T>
    8. struct HashNode
    9. {
    10. T _data;
    11. HashNode* _next;
    12. HashNode(const T& data)
    13. :_data(data)
    14. ,_next(nullptr)
    15. {}
    16. };
    17. template<class K,class T, class Hash, class KeyOfT>
    18. class HashTable
    19. {
    20. typedef HashNode Node;
    21. public:
    22. ~HashTable()
    23. {
    24. for (size_t i = 0; i < _tables.size(); ++i)
    25. {
    26. Node* cur = _tables[i];
    27. while (cur)
    28. {
    29. Node* next = cur->_next;
    30. delete cur;
    31. cur = next;
    32. }
    33. _tables[i] = nullptr;
    34. }
    35. }
    36. bool Insert(const T& data)
    37. {
    38. Hash hash;
    39. KeyOfT kot;
    40. //去重
    41. if (Find(kot(data)))
    42. {
    43. return false;
    44. }
    45. //负载因子到1就扩容
    46. if (_size == _tables.size())
    47. {
    48. size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
    49. vector newTables;
    50. newTables.resize(newSize, nullptr);
    51. //旧表中节点移动映射到新表
    52. for (size_t i = 0; i < _tables.size(); ++i)
    53. {
    54. Node* cur = _tables[i];
    55. while (cur)
    56. {
    57. Node* next = cur->_next;
    58. size_t hashi = hash(kot(cur->_data)) % newTables.size();
    59. cur->_next = newTables[hashi];
    60. newTables[hashi] = cur;
    61. cur = next;
    62. }
    63. _tables[i] = nullptr;
    64. }
    65. _tables.swap(newTables);
    66. }
    67. size_t hashi = hash(kot(data)) % _tables.size();
    68. //头插
    69. Node* newnode = new Node(data);
    70. newnode->_next = _tables[hashi];
    71. _tables[hashi] = newnode;
    72. ++_size;
    73. return true;
    74. }
    75. Node* Find(const K& key)
    76. {
    77. if (_tables.size() == 0)
    78. {
    79. return nullptr;
    80. }
    81. Hash hash;
    82. KeyOfT kot;
    83. size_t hashi = hash(key) % _tables.size();
    84. Node* cur = _tables[hashi];
    85. while (cur)
    86. {
    87. if (kot(cur->_data) == key)
    88. {
    89. return cur;
    90. }
    91. cur = cur->_next;
    92. }
    93. return nullptr;
    94. }
    95. bool Erase(const K& key)
    96. {
    97. if (_tables.size() == 0)
    98. {
    99. return false;
    100. }
    101. KeyOfT kot;
    102. Hash hash;
    103. size_t hashi = hash(key) % _tables.size();
    104. Node* prev = nullptr;
    105. Node* cur = _tables[hashi];
    106. while (cur)
    107. {
    108. if (kot(cur->_data) == key)
    109. {
    110. // 1、头删
    111. // 2、中间删
    112. if (prev == nullptr)
    113. {
    114. _tables[hashi] = cur->_next;
    115. }
    116. else
    117. {
    118. prev->_next = cur->_next;
    119. }
    120. delete cur;
    121. --_size;
    122. return true;
    123. }
    124. prev = cur;
    125. cur = cur->_next;
    126. }
    127. return false;
    128. }
    129. size_t Size()
    130. {
    131. return _size;
    132. }
    133. // 表的长度
    134. size_t TablesSize()
    135. {
    136. return _tables.size();
    137. }
    138. // 桶的个数
    139. size_t BucketNum()
    140. {
    141. size_t num = 0;
    142. for (size_t i = 0; i < _tables.size(); ++i)
    143. {
    144. if (_tables[i])
    145. {
    146. ++num;
    147. }
    148. }
    149. return num;
    150. }
    151. size_t MaxBucketLenth()
    152. {
    153. size_t maxLen = 0;
    154. for (size_t i = 0; i < _tables.size(); ++i)
    155. {
    156. size_t len = 0;
    157. Node* cur = _tables[i];
    158. while (cur)
    159. {
    160. ++len;
    161. cur = cur->_next;
    162. }
    163. //if (len > 0)
    164. //printf("[%d]号桶长度:%d\n", i, len);
    165. if (len > maxLen)
    166. {
    167. maxLen = len;
    168. }
    169. }
    170. return maxLen;
    171. }
    172. private:
    173. vector _tables;
    174. size_t _size = 0;//存储有效数据个数
    175. };
    176. }

    3.1 unorder_map 的封装

    1. #include"HashTable.h"//HashTable.h即为上面哈希桶的实现
    2. namespace zj
    3. {
    4. template<class K, class V, class Hash = HashFunc>
    5. class unorder_map
    6. {
    7. struct MapKeyOfT
    8. {
    9. const K& operator()(const pair& kv)
    10. {
    11. return kv.first;
    12. }
    13. };
    14. public:
    15. bool Insert(const pair& kv)
    16. {
    17. return _ht.Insert(kv);
    18. }
    19. private:
    20. Bucket::HashTable, Hash, MapKeyOfT> _ht;
    21. };
    22. }

    3.2 unorder_set 的封装

    1. #include"HashTable.h"//HashTable.h即为上面哈希桶的实现
    2. namespace zj
    3. {
    4. template<class K, class Hash = HashFunc>
    5. class unorder_set
    6. {
    7. struct SetKeyOfT
    8. {
    9. const K& operator()(const K& key)
    10. {
    11. return key;
    12. }
    13. };
    14. public:
    15. bool Insert(const K& key)
    16. {
    17. return _ht.Insert(key);
    18. }
    19. private:
    20. Bucket::HashTable _ht;
    21. };
    22. }

    3.3 哈希桶迭代器模板

    1. namespace Bucket
    2. {
    3. template<class T>
    4. struct HashNode
    5. {
    6. T _data;
    7. HashNode* _next;
    8. HashNode(const T& data)
    9. :_data(data)
    10. ,_next(nullptr)
    11. {}
    12. };
    13. //前置声明,不然__HashIterator里定义不了_pht
    14. template<class K, class T, class Hash, class KeyOfT>
    15. class HashTable;
    16. template<class K, class T, class Hash, class KeyOfT>
    17. struct __HashIterator
    18. {
    19. typedef HashNode Node;
    20. typedef HashTable HT;
    21. typedef __HashIterator Self;
    22. Node* _node;
    23. HT* _pht;
    24. __HashIterator(Node* node, HT* pht)
    25. :_node(node)
    26. ,_pht(pht)
    27. {}
    28. T& operator*()
    29. {
    30. return _node->_data;
    31. }
    32. T* operator->()
    33. {
    34. return &_node->_data;
    35. }
    36. Self& operator++()
    37. {
    38. if (_node->_next)
    39. {
    40. // 当前桶中迭代
    41. _node = _node->_next;
    42. }
    43. else
    44. {
    45. // 找下一个桶
    46. Hash hash;
    47. KeyOfT kot;
    48. size_t i = hash(kot(_node->_data)) % _pht->_tables.size();
    49. ++i;
    50. for (; i < _pht->_tables.size(); ++i)
    51. {
    52. if (_pht->_tables[i])
    53. {
    54. _node = _pht->_tables[i];
    55. break;
    56. }
    57. }
    58. // 说明后面没有有数据的桶了
    59. if (i == _pht->_tables.size())
    60. {
    61. _node = nullptr;
    62. }
    63. }
    64. return *this;
    65. }
    66. bool operator!=(const Self& s) const
    67. {
    68. return _node != s._node;
    69. }
    70. bool operator==(const Self& s) const
    71. {
    72. return _node == s._node;
    73. }
    74. };
    75. template<class K,class T, class Hash, class KeyOfT>
    76. class HashTable
    77. {
    78. typedef HashNode Node;
    79. template<class K, class T, class Hash, class KeyOfT>//模板参数友元要带声明
    80. friend struct __HashIterator;
    81. public:
    82. typedef __HashIterator iterator;
    83. iterator begin()
    84. {
    85. for (size_t i = 0; i < _tables.size(); ++i)
    86. {
    87. if (_tables[i])
    88. {
    89. return iterator(_tables[i], this);
    90. }
    91. }
    92. return end();
    93. }
    94. iterator end()
    95. {
    96. return iterator(nullptr, this);
    97. }
    98. ~HashTable()
    99. {
    100. for (size_t i = 0; i < _tables.size(); ++i)
    101. {
    102. Node* cur = _tables[i];
    103. while (cur)
    104. {
    105. Node* next = cur->_next;
    106. delete cur;
    107. cur = next;
    108. }
    109. _tables[i] = nullptr;
    110. }
    111. }
    112. pairbool> Insert(const T& data)
    113. {
    114. Hash hash;
    115. KeyOfT kot;
    116. // 去重
    117. iterator ret = Find(kot(data));
    118. if (ret != end())
    119. {
    120. return make_pair(ret, false);
    121. }
    122. //负载因子到1就扩容
    123. if (_size == _tables.size())
    124. {
    125. size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
    126. vector newTables;
    127. newTables.resize(newSize, nullptr);
    128. //旧表中节点移动映射到新表
    129. for (size_t i = 0; i < _tables.size(); ++i)
    130. {
    131. Node* cur = _tables[i];
    132. while (cur)
    133. {
    134. Node* next = cur->_next;
    135. size_t hashi = hash(kot(cur->_data)) % newTables.size();
    136. cur->_next = newTables[hashi];
    137. newTables[hashi] = cur;
    138. cur = next;
    139. }
    140. _tables[i] = nullptr;
    141. }
    142. _tables.swap(newTables);
    143. }
    144. size_t hashi = hash(kot(data)) % _tables.size();
    145. //头插
    146. Node* newnode = new Node(data);
    147. newnode->_next = _tables[hashi];
    148. _tables[hashi] = newnode;
    149. ++_size;
    150. return make_pair(iterator(newnode, this), true);
    151. }
    152. iterator Find(const K& key)
    153. {
    154. if (_tables.size() == 0)
    155. {
    156. return end();
    157. }
    158. Hash hash;
    159. KeyOfT kot;
    160. size_t hashi = hash(key) % _tables.size();
    161. Node* cur = _tables[hashi];
    162. while (cur)
    163. {
    164. if (kot(cur->_data) == key)
    165. {
    166. return iterator(cur, this);
    167. }
    168. cur = cur->_next;
    169. }
    170. return end();
    171. }
    172. bool Erase(const K& key)
    173. {
    174. if (_tables.size() == 0)
    175. {
    176. return false;
    177. }
    178. KeyOfT kot;
    179. Hash hash;
    180. size_t hashi = hash(key) % _tables.size();
    181. Node* prev = nullptr;
    182. Node* cur = _tables[hashi];
    183. while (cur)
    184. {
    185. if (kot(cur->_data) == key)
    186. {
    187. // 1、头删
    188. // 2、中间删
    189. if (prev == nullptr)
    190. {
    191. _tables[hashi] = cur->_next;
    192. }
    193. else
    194. {
    195. prev->_next = cur->_next;
    196. }
    197. delete cur;
    198. --_size;
    199. return true;
    200. }
    201. prev = cur;
    202. cur = cur->_next;
    203. }
    204. return false;
    205. }
    206. ....
    207. private:
    208. vector _tables;
    209. size_t _size = 0;//存储有效数据个数
    210. };
    211. }

    3.4 unordered_map 加迭代器的封装

    1. #include"HashTable.h"
    2. namespace zj
    3. {
    4. template<class K, class V, class Hash = HashFunc>
    5. class unordered_map
    6. {
    7. struct MapKeyOfT
    8. {
    9. const K& operator()(const pair& kv)
    10. {
    11. return kv.first;
    12. }
    13. };
    14. public:
    15. typedef typename Bucket::HashTable, Hash, MapKeyOfT>::iterator iterator;
    16. iterator begin()
    17. {
    18. return _ht.begin();
    19. }
    20. iterator end()
    21. {
    22. return _ht.end();
    23. }
    24. pairbool> Insert(const pair& kv)
    25. {
    26. return _ht.Insert(kv);
    27. }
    28. V& operator[](const K& key)
    29. {
    30. pairbool> ret = _ht.Insert(make_pair(key, V()));
    31. return ret.first->second;
    32. }
    33. private:
    34. Bucket::HashTable, Hash, MapKeyOfT> _ht;
    35. };
    36. void test_map()
    37. {
    38. unordered_map dict;
    39. dict.Insert(make_pair("sort", ""));
    40. dict.Insert(make_pair("string", ""));
    41. dict.Insert(make_pair("left", ""));
    42. unordered_map::iterator it = dict.begin();
    43. while (it != dict.end())
    44. {
    45. cout << it->first << ":" << it->second << endl;
    46. ++it;
    47. }
    48. cout << endl;
    49. }
    50. }

    3.5 unordered_set 加迭代器的封装

    1. #include"HashTable.h"
    2. namespace zj
    3. {
    4. template<class K, class Hash = HashFunc>
    5. class unorder_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::iterator iterator;
    16. iterator begin()
    17. {
    18. return _ht.begin();
    19. }
    20. iterator end()
    21. {
    22. return _ht.end();
    23. }
    24. pairbool> Insert(const K& key)
    25. {
    26. return _ht.Insert(key);
    27. }
    28. private:
    29. Bucket::HashTable _ht;
    30. };
    31. void test_set()
    32. {
    33. unordered_set<int> s;
    34. s.insert(2);
    35. s.insert(3);
    36. s.insert(1);
    37. s.insert(2);
    38. s.insert(5);
    39. unordered_set<int>::iterator it = s.begin();
    40. //auto it = s.begin();
    41. while (it != s.end())
    42. {
    43. cout << *it << " ";
    44. ++it;
    45. }
    46. cout << endl;
    47. }
    48. }

  • 相关阅读:
    Java日志框架log4j、logback、jul这么多?到底如何选择,他们之间有有什么关联?如何整合使用?
    XMLGregorianCalendar与Date互转
    从零用VitePress搭建博客教程(5) - 如何自定义页面模板、给页面添加独有的className和使页面标题变成侧边目录?
    Mysql binlog的三种模式statement,row,mixed详解,以及无主键造成复制延时的测试
    【QT】QListWidget
    空心三角形
    C++实现单链表的各种操作(数据结构)
    2023燕山大学计算机考研信息汇总
    手机备忘录可以设置密码吗 能锁屏加密的备忘录
    【深度学习】ONNX模型快速部署【入门】
  • 原文地址:https://blog.csdn.net/blue_jun_/article/details/127809151