当前位置:   article > 正文

字符串/超大数据的哈希(hash)高效实现_长字符串的hash策略

长字符串的hash策略

C++代码中有unordered_map<>()可实现hash操作,但是在追求效率的代码中,使用会严重影响代码的执行效率。

特别当需要hash字符串的时候,可能有人会这么操作:

  • unordered_map<string, int> hash_map;

上述操作确实没有错误,但是当你在C++中追求高效率的时候,这种写法可能会比较耗时。这里推荐一个利用数组和双向循环链表实现哈希的方法。

通常的hash操作有字符串哈希和较大数据的哈希。

  • 字符串哈希,通常利用移位操作。hval = (hval << 5) + (str[i] - 'a' + 1),同时需要将 hval 进行缩放,这里一般可以对13131(较大的质数,实验发现哈希冲突较小)或者2的次方(CPU计算除法时对于2的次方速度较快)取余。
  • 对较大的数据哈希,一般也是对13131或者2的次方取余。(这其实也就是hash table的范围)

字符串哈希

  1. #define HASHSIZE 10
  2. #define ull unsigned long long
  3. int getHashVal(char* str) {
  4. int i = 0;
  5. ull hval = 0;
  6. while (str[i] != '\0') {
  7. hval = (hval << 5) + (str[i] - 'a' + 1);
  8. ++i;
  9. }
  10. // 当然这里可以直接返回hval,但是直接的返回值无法通过数组的index进行建立hash table.
  11. return hval % HASHSIZE;
  12. }

当然上述方法只适用于str全由小写字母组成的字符串哈希,对于大小写混合后者纯大写字母组成的字符串仅仅需要依据需求修改移位操作就可以。这里仅仅以纯小写字母为例进行算法说明。

(str[i] - 'a' + 1) 可以使得任何一个小写字母均可以由1~26的数字来表示,切记这里需要+1,否则你讲无法区别“a”和“aa”等。

(hval << 5) 左移5位正好可以完全表示1~26,当然也有部分剩余。

(unsigned long long) 最大可以表示64byte的数据,所以这种hash方法一般用来表示字符串长度不超过12的情况。

在这里,我们仅仅是对字符串进行了哈希操作(字符串转整形),但在实际应用中还设计哈希的冲突解决以及检索等,下面我们接着阐述后续内容。

哈希存储结构

char str[] 存储需要哈希的字符串信息。同时需要记录节点的头尾指针。

  1. struct Node {
  2. char str[11];
  3. int value;
  4. Node* next;
  5. Node* prev;
  6. };
  7. Node hash_table[HASHSIZE];

哈希查找

这里为了解决哈希冲突,我们引入了双向循环链表(即将冲突的节点插入到循环链表中)。需要结合双向链表进行操作(具体在哈希冲突解决节中详细阐述)。

当找到str对应的字符串时返回对应节点的指针,当没有找到(哈希表中不存在)时返回空指针nullptr

  1. Node* findHash(char* str) {
  2. int hash = getHashVal(str);
  3. Node* find_node = hash_table[hash].next;
  4. while (find_node != &hash_table[hash]) {
  5. if (strcmp(find_node->str, str) == 0) {
  6. break;
  7. }
  8. find_node = find_node->next;
  9. }
  10. if (find_node == &hash_table[hash]) {
  11. return nullptr;
  12. }
  13. return find_node;
  14. }

哈希冲突解决(哈希插入更新)

主要通过函数insertHash来实现插入更新,需要基于双向循环链表。

没有时需要插入新Node,当已有时进行响应的更新操作即可(当然这里需要根据自己的具体需求进行修改完善)。

  1. void _add(Node* now, Node* prev, Node* next) {
  2. now->prev = prev;
  3. now->next = next;
  4. prev->next = now;
  5. next->prev = now;
  6. }
  7. void addNode(Node* now, Node* head) {
  8. _add(now, head, head->next);
  9. }
  10. void insertHash(char* str, int value) {
  11. int hash = getHashVal(str);
  12. Node* node = findHash(str);
  13. if (node == nullptr) {
  14. Node* new_node = new Node;
  15. strcpy(new_node->str, str);
  16. new_node->value = value;
  17. addNode(new_node, &hash_table[hash]);
  18. }
  19. else {
  20. node->value = value;
  21. }
  22. }

以上是hash table的具体实现方法,当然在C++中可以将其封装为类,以便使用中更加方便。这里提供一种封装策略,当然这个的泛化性还需更一步改进。

Hash table 整体实现

  1. #define ull unsigned long long
  2. struct Node {
  3. char str[11];
  4. int value;
  5. Node* next;
  6. Node* prev;
  7. };
  8. template<int HASHSIZE>
  9. class HashTable {
  10. public:
  11. void initHash() {
  12. for (int i = 0; i < HASHSIZE; ++i) {
  13. hash_table[i].next = hash_table[i].prev = &hash_table[i];
  14. }
  15. }
  16. Node* findHash(char* str) {
  17. int hash = getHashVal(str);
  18. Node* find_node = hash_table[hash].next;
  19. while (find_node != &hash_table[hash]) {
  20. if (strcmp(find_node->str, str) == 0) {
  21. break;
  22. }
  23. find_node = find_node->next;
  24. }
  25. if (find_node == &hash_table[hash]) {
  26. return nullptr;
  27. }
  28. return find_node;
  29. }
  30. void insertHash(char* str, int value) {
  31. int hash = getHashVal(str);
  32. Node* node = findHash(str);
  33. if (node == nullptr) {
  34. Node* new_node = new Node;
  35. strcpy(new_node->str, str);
  36. new_node->value = value;
  37. addNode(new_node, &hash_table[hash]);
  38. }
  39. else {
  40. node->value = value;
  41. }
  42. }
  43. private:
  44. int getHashVal(char* str) {
  45. int i = 0;
  46. ull hval = 0;
  47. while (str[i] != '\0') {
  48. hval = (hval << 5) + (str[i] - 'a' + 1);
  49. ++i;
  50. }
  51. return hval % HASHSIZE;
  52. }
  53. inline void _add(Node* now, Node* prev, Node* next) {
  54. now->prev = prev;
  55. now->next = next;
  56. prev->next = now;
  57. next->prev = now;
  58. }
  59. void addNode(Node* now, Node* head) {
  60. _add(now, head, head->next);
  61. }
  62. Node hash_table[HASHSIZE];
  63. };

相关资料:

图文并茂详解数据结构之哈希表 - 知乎 (zhihu.com)

来吧!一文彻底搞定哈希表! - 知乎 (zhihu.com)

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

闽ICP备14008679号