目录

1.引例:两个练习题

2.定义基本的存储结构

3.Insert()

4.Erase()

5.string为参数的情况

6.添加类模板

7.整体代码


1.引例:两个练习题

2.定义基本的存储结构

#pragma once
namespace CloseHash
{template<class K,class V>struct HashData{pair<K, V> _kv;};template<class K,class V>class HashTable{private:HashData* _table;size_t _size;size_t _capacity; //用于增容};
}

后者直接用vector存储

#pragma once
#include<vector>
#include<iostream>
using namespace std;
namespace CloseHash
{template<class K,class V>struct HashData{pair<K, V> _kv;};template<class K,class V>class HashTable{public://待写private://HashData* _table;//size_t _size;//size_t _capacity; //用于增容vector<HashData> _table;size_t _n;//存储的有效数据的个数};
}

3.Insert()

如何判断一个位置是空还是满?若删除其中一个值,用空值去标识,下一次找后面冲突的值找不到了。前一个冲突的值被删了,导致不连续了,找到空就结束了。

提供一个状态标识即可

 namespace CloseHash
{enum State//状态{EMPTY,EXITS,DELTE,};template<class K,class V>struct HashData{pair<K, V> _kv;State _state = EMPTY;//默认状态给空};template<class K,class V>class HashTable{public://待写函数private://HashData* _table;//size_t _size;//size_t _capacity; //用于增容vector<HashData> _table;size_t _n;//存储的有效数据的个数};
}
 bool Insert(const pair<K, V>& kv)
{HashData<K, V>* ret = Find(kv, first);if (ret){return false;}//判断是否满//计算负载因子,大于0.7就增容//if (_n*10 / _table.size() > 7)if (_tabale.size() == 0){_table.resize(10);}else if ((double)_n / (double)_table.size() > 0.7){//方法一,不足之处在于要重复写插入逻辑//vector<HashData>newtable;//newtable.resize(_table.size*2);  新表增容,增到旧表的二倍//for (auto& e : _table)//{//  if (e._table == EXITS)//  {//     //重新计算放到newtable中去//        //跟下面插入逻辑类似//   }//}//_table.swap(newtable);HashTable<K, v>newHT;newHT._table.resize(_table.size() * 2);for (auto& e : _table){if (e._state == EXITS){newHT.Insert(e._kv);}}_table.swap(newHT._table);}size_t start = kv.first % _table.size();//不能%_table.capacity()size_t index = start;//探测后面的位置 -- 线性探测或二次探测size_t i = 1;while (_table[index]._state == EXITS){index += start + i;//此处的线性探测想改成二次探测将i改为i*i即可index %= _table.size();++i;}_table[index]._kv = kv;_table[index]._state = EXITS;++_n;return true;
}HashData<K, V>* Find(const K& key)
{//排除0的情况if (_table.size() == 0){return nullptr;}size_t start = key % _table.size();size_t index = start;size_t i = 1;while (_table[index]._state != EMPTY){if (_table[index]._state == EXITS//避免已经删除的元素仍可被寻找的情况&& _table[index]._kv.first == key){return &_table[index];}inded = start + i;index %= _table.size();++i;}return nullptr;
}

4.Erase()

bool Erase(const K& key)
{HashData<K, V>* ret = Find(key);if (ret == nullptr){return false;}else{ret->_state = DELETE;return true;}
}

5.string为参数的情况

若出现下面测试代码的情况,如何更改?

void TestHashTable2()
{string a[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "橘子", "苹果" };HashTable<string, int> ht;for (auto str : a)//string作k的情况{auto ret = ht.Find(str);if (ret){ret->_kv.second++;}else{ht.Insert(make_pair(str, 1));}}
}

所有要取模的地方添加仿函数,该仿函数的功能就是将数据转换成整型。

struct IniHashFunc
{int operator()(int i){return i;}
};

特别的,将字符串转换成整型的仿函数仍存在问题。采用字符串哈希算法的思路改正即可    各种字符串Hash函数 - clq - 博客园

以此类推,K为不同类型元素的情况写对应的仿函数即可

思考:

一个类型去作map/set的Key有什么要求? 能支持比较大小

一个类型去作unordered_map/unordered_set的Key有什么要求?  能支持转换成整型+相等比较

6.添加类模板

可以转化成整型的数据采用下面模板,返回值为int

template<class K>
struct Hash
{size_t operator()(const K& key){return key;}
};

当K是string时不会走原生的模板,采用特化单独创建一个模板

template<>
struct Hash<string>
{//字符串转成对应的整型,整型才能取模算映射位置//期望->字符串不同,转出的整型值尽量不同size_t operator()(const string& s){//return s[0];  存在int insert类似首字母相同的情况//存在abcd,bcad或abbb.abca的情况(ascii码和相同的情况)size_t value = 0;for (auto ch : s){value += ch;value *= 131;//采用BKDRHash算法的思想 乘以131}return value;}
};

使用类模板后不同的数据类型会调用不同类型的仿函数

此时可以省略仿函数,参数自动使用隐式类型转换

7.整体代码

#pragma once
#include <vector>
#include <iostream>
using namespace std;namespace CloseHash
{enum State{EMPTY,EXITS,DELETE,};template<class K, class V>struct HashData{pair<K, V> _kv;State _state = EMPTY;  // 状态};template<class K>struct Hash{size_t operator()(const K& key){return key;}};// 特化template<>struct Hash<string>{// "int"  "insert" // 字符串转成对应一个整形值,因为整形才能取模算映射位置// 期望->字符串不同,转出的整形值尽量不同// "abcd" "bcad"// "abbb" "abca"size_t operator()(const string& s){// BKDR Hashsize_t value = 0;for (auto ch : s){value += ch;value *= 131;}return value;}};template<class K, class V, class HashFunc = Hash<K>>class HashTable{public:bool Insert(const pair<K, V>& kv){HashData<K,V>* ret = Find(kv.first);if (ret){return false;}// 负载因子大于0.7,就增容//if (_n*10 / _table.size() > 7)if (_table.size() == 0){_table.resize(10);}else if ((double)_n / (double)_table.size() > 0.7){//vector<HashData> newtable;// newtable.resize(_table.size*2);//for (auto& e : _table)//{//   if (e._state == EXITS)//  {//     // 重新计算放到newtable//     // ...跟下面插入逻辑类似//   }//}//_table.swap(newtable);HashTable<K, V, HashFunc> newHT;newHT._table.resize(_table.size() * 2);for (auto& e : _table){if (e._state == EXITS){newHT.Insert(e._kv);}}_table.swap(newHT._table);}HashFunc hf;size_t start = hf(kv.first) % _table.size();size_t index = start;// 探测后面的位置 -- 线性探测 or 二次探测size_t i = 1;while (_table[index]._state == EXITS){index = start + i;index %= _table.size();++i;}_table[index]._kv = kv;_table[index]._state = EXITS;++_n;return true;}HashData<K,V>* Find(const K& key){if (_table.size() == 0){return nullptr;}HashFunc hf;size_t start = hf(key) % _table.size();size_t index = start;size_t i = 1;while (_table[index]._state != EMPTY){if (_table[index]._state == EXITS && _table[index]._kv.first == key){return &_table[index];}index = start + i;index %= _table.size();++i;}return nullptr;}bool Erase(const K& key){HashData<K, V>* ret = Find(key);if (ret == nullptr){return false;}else{ret->_state = DELETE;return true;}}private:/*    HashData* _table;size_t _size;size_t _capacity;*/vector<HashData<K, V>> _table;size_t _n = 0;  // 存储有效数据的个数};void TestHashTable1(){int a[] = { 1, 5, 10, 100000, 100, 18, 15, 7, 40};HashTable<int, int> ht;for (auto e : a){ht.Insert(make_pair(e, e));}auto ret = ht.Find(100);if (ret){cout << "找到了"<<endl;}else{cout << "没有找到了" << endl;}ht.Erase(100);ret = ht.Find(100);if (ret){cout << "找到了" << endl;}else{cout << "没有找到了" << endl;}}struct stringHashFunc{// "int"  "insert" // 字符串转成对应一个整形值,因为整形才能取模算映射位置// 期望->字符串不同,转出的整形值尽量不同// "abcd" "bcad"// "abbb" "abca"size_t operator()(const string& s){// BKDR Hashsize_t value = 0;for (auto ch : s){value += ch;value *= 131;}return value;}};void TestHashTable2(){string a[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "橘子", "苹果" };HashTable<string, int> ht;for (auto str : a){auto ret = ht.Find(str);if (ret){ret->_kv.second++;}else{ht.Insert(make_pair(str, 1));}}}struct Student{// ...};struct StudentHashFunc{size_t operator()(const Student& kv){// 如果是结构体// 1、比如说结构体中有一个整形,基本是唯一值 - 学号// 2、比如说结构体中有一个字符串,基本是唯一值 - 身份证号// 3、如果没有一项是唯一值,可以考虑多项组合size_t value = 0;// ...return value;}};void TestHashTable3(){string a[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "橘子", "苹果" };// 任意类型都可以做key,跟上一个把这个类型对象转换成整形的仿函数即可HashTable<Student, int, StudentHashFunc> ht;}void TestStringHashFunc(){stringHashFunc hf;cout << hf("insert")<<endl;cout << hf("int") << endl<<endl;cout << hf("abcd") << endl;cout << hf("bacd") << endl << endl;cout << hf("abbb") << endl;cout << hf("abca") << endl << endl;}
}

C++ 哈希表模拟实现(补充)相关推荐

  1. 电话号码 (哈希表+模拟)

    1.题目引入: 一个电话销售员正在整理他的电话簿. 电话簿中记录了他的全部客户的电话号码. 一个客户可能有不止一个电话号码. 不同客户可能拥有完全相同的电话号码. 电话簿中一共包含 nn 条记录. 每 ...

  2. 【代码随想录】二刷-哈希表

    哈希表 <代码随想录> 哈希表一般用来快速查找某个元素是否在一个集合中. 如果使用枚举的话时间复杂度为O(n),而使用哈希表只O(1)就可以做到.--元素查询. 242.有效的字母异位词 ...

  3. 【算法】 哈希表 自己模拟hashMap

    文章目录 1.概述 2.Hash表 3. 模拟 1.概述 视频地址:https://www.bilibili.com/video/BV1E4411H73v?p=86 哈希表(散列)-Google.上机 ...

  4. Map和Set,简单模拟实现哈希表以及哈希表部分底层源码的分析

    目录 Map和Set的简单介绍 降低哈希冲突发生的概率以及当冲突发生时如何解决哈希冲突 简单模拟实现哈希表--1.key为整形:2.key为引用类型 哈希表部分底层源码的分析 1.Map和Set的简单 ...

  5. C++--哈希表--散列--冲突--哈希闭散列模拟实现--问答--1107

    1.哈希 概念 可以不经过任何比较,直接从表中得到要搜索的元素. 关键在于通过某种散列函数,使元素的存储位置与它的关键码之间能够建立 一一映射的关系.这样就可以通过o(1)的时间复杂度来寻找到元素. ...

  6. 哈希表(模拟散列表 字符串哈希)

    目录 一.哈希表的概念 二.模拟散列表 题目 代码实现 ①拉链法 ②开放寻址法 三.字符串哈希 题目 思路 注意点 代码实现 一.哈希表的概念 哈希表(又称为散列表),将一个比较大的值域映射到一个小的 ...

  7. CSP认证201509-3 模板生成系统[C++题解]:字符串处理、模拟、哈希表、引号里面有空格的字符串怎么读入

    题目分析 来源:acwing 分析: 本题采用vector< string > 来读入原来模板.接下来的m行需要用到哈希表,进行模板和具体内容的映射. 遍历vector,如果找到{{,就对 ...

  8. C++ 使用哈希表封装模拟实现unordered_map unordered_set

    一.unordered_map unordered_set 和 map set的区别 1. map set底层采取的红黑树的结构,unordered_xxx 底层数据结构是哈希表.unordered_ ...

  9. JavaScript数据结构与算法(2)(集合、字典、哈希表、二叉树、图)(ES6)

    注意:原教学视频:JavaScript(ES6)数据结构和算法 | JavaScript数据结构与算法 (都是CoderWhy老师的教学) 原作者(笔记)链接:JavaScript 数据结构与算法 | ...

最新文章

  1. PHP如何更好的利用PHPstorm的自动提示
  2. windows7 下 安装ipython-5.1.0报错
  3. linux 下使用 curl 访问带多参数,GET掉参数解决方案
  4. SpringMVC 4.0常用注解
  5. java 代码效率_Java效率
  6. STM32下一次程序后J-link不能识别问题解决
  7. 2020年周记(1/50)
  8. pytorch梯度下降函数_Pytorch中常用的四种优化器SGD、Momentum、RMSProp、Adam
  9. 图像算法八:【图像分割】边缘检测(Roberts,Sobel,canny)、霍夫变换(hough)、阈值分割、区域分割
  10. C++学习笔记(14) static_cast 与 dynamic_cast
  11. WinEdt 参考文献格式
  12. maya python插件_使用Python开发Maya导出插件的一些辅助
  13. 今天你又丧了吗? | Alfred数据室
  14. 程序员如何搭建自己的知识体系?
  15. iOS之地图的使用和实时描绘运动轨迹
  16. 日常pytho3练习脚本之--两个逗比聊天机器人
  17. 【网页制作】CSS文本和字体属性讲解【附讲解视频】
  18. linux 守护进程小结
  19. js实现小球抛物线运动
  20. leetcode974. 和可被 K 整除的子数组

热门文章

  1. 手机上如何将图片转换成PDF文档
  2. python统计次数正则_Python提取信息必学基础——正则表达式
  3. lvgl v8 line_meter
  4. 世界是由什么组成的java_世界是由什么组成的?
  5. 微软服务器系统ssd,免费试用微软2个月Windows 365云桌面,4核心+16G内存+128SSD
  6. 场景:一个年级,相当链表A ,该年级5个班,每个班5个人,相当于链表B1--B5:做一个学生成绩管理系统学生成绩有语文 数学 英语功能: 录入成绩 找三科总分的最高分 最低分 算出平均分
  7. 算法补完计划(五) 二分图匹配
  8. 带你走进程序猿的内心世界
  9. Re: 征男友征男友(有女友者勿回) (转载)
  10. 阿里放弃SpringCloud、Dubbo,选择的这个神仙框架!牛逼