• hash:哈希表 哈希桶


    目录

    1.哈希的思想

    2.解决冲突

    3.哈希表(采用的闭散列,线性探测)

    4.哈希桶(开散列)

    5.总结


    1.哈希的思想

    hash是hash是一种根据存储的关键字的哈希值确定存储位置的算法。哈希值通过哈希函数可以计算得出结果,最常用的哈希函数就是除留余数法了。


    2.解决冲突

    如果俩个关键字值不同但是哈希值相同就会发生不明确存哪的问题,这种情况被称为哈希冲突。

    解决冲突的方式1:闭散列,采用用线性探测,即跳过被占用的按顺序向下探测位置。

    解决冲突的方式2:开散列,由于是链式结构,所以直接在对应位置串起来即可。


    3.哈希表(采用的闭散列,线性探测

    上菜:

    1. enum state
    2. {
    3. empty, //表示空状态
    4. exist, //表示占据状态
    5. dlete //表示删除状态
    6. };
    7. template<class K>
    8. class HashFunc //仿函数,用来计算关键字的hash值
    9. {
    10. int operator()(const K& key)
    11. {
    12. return key;
    13. }
    14. };
    15. template<> //特化,采用string的assic码来计算
    16. class HashFunc
    17. {
    18. int operator()(const string& key)
    19. {
    20. int val = 0;
    21. for (auto e : key)
    22. {
    23. val *= 131; //结论,用数学公式证明的,用就完了。
    24. val += e;
    25. }
    26. return val;
    27. }
    28. };
    29. template<class K,class V>
    30. struct hashnode
    31. {
    32. state st=empty;
    33. pair _kv;
    34. };
    35. template<class K,class V,class Hash=HashFunc>
    36. class hashtable
    37. {
    38. private:
    39. vector> _table;
    40. int size = 0;
    41. public:
    42. bool insert(const pair& kv) //插入
    43. {
    44. if (_table.size() == 0 || 10*size/_table.size() >= 7) //负载因子确定为0.7
    45. {
    46. int newsize = _table.size() == 0 ? 10 : _table.size() = _table.size() * 2;
    47. hashtable newtable; //建立新表
    48. newtable._table.resize(newsize); //修改长度
    49. for (auto e : _table)
    50. {
    51. //将旧表元素插入到新表中
    52. if (e.st == empty)
    53. {
    54. newtable.insert(e._kv);
    55. }
    56. }
    57. _table.swap(newtable._table);//新旧表交换
    58. }
    59. Hash hash;
    60. int val=hash(kv.first); //计算下标
    61. int hashi = val % (_table.size());
    62. while (_table[hashi].st == exist) //已经被占据了就继续查找
    63. {
    64. hashi++;
    65. hashi %= (_table.size()); //防止越界
    66. }
    67. _table[hashi]._kv = kv;
    68. _table[hashi].st = exist; //记得标记为占有状态
    69. size++;
    70. return true;
    71. }
    72. hashnode* find(const K& key) //要找一个循环,因为元素可能发生了冲突
    73. {
    74. Hash hash;
    75. int start = hash(key) % (_table.size()); //标记起始位置
    76. int hashi = start;
    77. while (hashi != start && _table[hashi].st != empty)//不能为空位置
    78. {
    79. if (_table[hashi].st != delete && _table[hashi]._kv.first == key)
    80. {
    81. return &_table[hashi];
    82. }
    83. hashi++;
    84. hashi %= (_table.size());
    85. }
    86. return nullptr;
    87. }
    88. bool erase(const K& key)
    89. {
    90. hashnode* ret = find(key); //复用find查找位置
    91. if (ret)
    92. {
    93. ret->st = dlete;
    94. --size; //记得--size
    95. return true;
    96. }
    97. return false;
    98. }
    99. void print() //打印看看结果
    100. {
    101. for (size_t i = 0; i < _tables.size(); ++i)
    102. {
    103. if (_tables[i].st == exist)
    104. {
    105. printf("[%d:%d] ", i, _tables[i]._kv.first);
    106. }
    107. else
    108. {
    109. printf("[%d:*] ", i);
    110. }
    111. }
    112. cout << endl;
    113. }
    114. };

    思路非常简单,先用hash函数找到对应位置。被占住了就继续向后找空位置。写这段代码需要注意的是在扩容时,由于长度的变化,需要映射新的位置。可以采用复用insert的方式完成映射。


    4.哈希桶(开散列)

    上菜:

    1. #pragma once
    2. #include
    3. #include
    4. using namespace std;
    5. template<class K>
    6. struct HashFunc
    7. {
    8. int operator()(const K& key)
    9. {
    10. return key;
    11. }
    12. };
    13. template<>
    14. struct HashFunc
    15. {
    16. int operator()(const string& key)
    17. {
    18. int val = 0;
    19. for (auto e : key)
    20. {
    21. val *= 131;
    22. val += e;
    23. }
    24. return val;
    25. }
    26. };
    27. template<class K, class V>
    28. struct HashNode //链式结构要创建结点
    29. {
    30. pair _kv;
    31. HashNode* _next;
    32. HashNode(pair kv)
    33. :_kv(kv)
    34. , _next(nullptr)
    35. {}
    36. };
    37. template<class K,class V,class Hash=HashFunc>
    38. class Hashtable
    39. {
    40. private:
    41. vector*> _table;
    42. int size=0;
    43. public:
    44. ~Hashtable()
    45. {
    46. for (int i = 0; i < _table.size(); i++)
    47. {
    48. HashNode* cur = _table[i];
    49. while (cur)
    50. {
    51. HashNode* next = cur->_next; //记录下一个位置
    52. delete cur;
    53. cur = next;
    54. }
    55. _table[i] = nullptr; //记得置空
    56. }
    57. }
    58. HashNode* find(const K& key)
    59. {
    60. Hash hash;
    61. if (_table.size() == 0)
    62. {
    63. return nullptr;
    64. }
    65. int hashi = hash(key) % _table.size(); //计算位置
    66. HashNode* cur = _table[hashi];
    67. while (cur)
    68. {
    69. if (cur->_kv.first == key) //找到
    70. {
    71. return cur;
    72. }
    73. cur = cur->_next;
    74. }
    75. return nullptr;
    76. }
    77. bool insert(const pair& kv)
    78. {
    79. if (find(kv.first)) //去重
    80. return false;
    81. if (_table.size() == size || _table.size() == 0) //扩容
    82. {
    83. int newsize = (_table.size() == 0 ? 10 : _table.size() * 2);
    84. Hashtable newtable; //构建新表
    85. newtable._table.resize(newsize);
    86. for (int i = 0; i < _table.size(); i++)
    87. {
    88. //不能浪费空间,要将原来的资源直接插入到新表就不可以复用。
    89. HashNode* cur = _table[i];
    90. while (cur)
    91. {
    92. HashNode* next = cur->_next;
    93. Hash hash;
    94. int hashi = hash(cur->_kv.first)%(newtable._table.size());
    95. cur->_next = newtable._table[hashi];
    96. newtable._table[hashi] = cur;
    97. cur = next;
    98. }
    99. }
    100. _table.swap(newtable._table);
    101. }
    102. Hash hash;
    103. int hashi = hash(kv.first)%_table.size();
    104. HashNode* newnode = new HashNode(kv);
    105. //头插
    106. newnode->_next = _table[hashi];
    107. _table[hashi] = newnode;
    108. ++size; //记得++size
    109. }
    110. };

    同样是先用hash函数找到对应位置,不同的是,可以直接头插不需要移位。唯一需要注意的依然是扩容,这里不可以复用代码,因为旧表的空间必须利用上,不然会出现资源泄露的问题。


    5.总结

    hash(链式)相对于平衡搜索数(avl 红黑树)有优有劣。

    优点:a.对于查找来说,平衡搜索树时间复杂度是O(logn)。而hash的时间复杂度趋近O(1)。

               b.对于插入来说,平衡搜索树需要不断的翻转。而hash直接头插即可。

    缺点:a.由于搜索树的性质决定了搜索树可以将数据有序化,而hash是无序的。

               b.随着数据的增大,hash空间消耗会大于平衡搜索树。

  • 相关阅读:
    CentOS安装Docker
    【微服务的集成测试】python实现-附ChatGPT解析
    java计算机毕业设计ssm课程建设制作服务平台系统
    冰冰学习笔记:快速排序
    微服务项目:尚融宝(21)(后端搭建:OSS文件上传整合2)
    Rust通用编程概念(3)
    proxmox8.1更换源
    304-314Coderwhy-JavaScript学习教程变量的定义命名规则,练习
    AOP进阶-连接点
    数据库管理-第四十二期 复盘一下(20221104)
  • 原文地址:https://blog.csdn.net/xiao_xiao21/article/details/128031841