C++ 哈希表模拟实现(补充)
目录
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.题目引入: 一个电话销售员正在整理他的电话簿. 电话簿中记录了他的全部客户的电话号码. 一个客户可能有不止一个电话号码. 不同客户可能拥有完全相同的电话号码. 电话簿中一共包含 nn 条记录. 每 ...
- 【代码随想录】二刷-哈希表
哈希表 <代码随想录> 哈希表一般用来快速查找某个元素是否在一个集合中. 如果使用枚举的话时间复杂度为O(n),而使用哈希表只O(1)就可以做到.--元素查询. 242.有效的字母异位词 ...
- 【算法】 哈希表 自己模拟hashMap
文章目录 1.概述 2.Hash表 3. 模拟 1.概述 视频地址:https://www.bilibili.com/video/BV1E4411H73v?p=86 哈希表(散列)-Google.上机 ...
- Map和Set,简单模拟实现哈希表以及哈希表部分底层源码的分析
目录 Map和Set的简单介绍 降低哈希冲突发生的概率以及当冲突发生时如何解决哈希冲突 简单模拟实现哈希表--1.key为整形:2.key为引用类型 哈希表部分底层源码的分析 1.Map和Set的简单 ...
- C++--哈希表--散列--冲突--哈希闭散列模拟实现--问答--1107
1.哈希 概念 可以不经过任何比较,直接从表中得到要搜索的元素. 关键在于通过某种散列函数,使元素的存储位置与它的关键码之间能够建立 一一映射的关系.这样就可以通过o(1)的时间复杂度来寻找到元素. ...
- 哈希表(模拟散列表 字符串哈希)
目录 一.哈希表的概念 二.模拟散列表 题目 代码实现 ①拉链法 ②开放寻址法 三.字符串哈希 题目 思路 注意点 代码实现 一.哈希表的概念 哈希表(又称为散列表),将一个比较大的值域映射到一个小的 ...
- CSP认证201509-3 模板生成系统[C++题解]:字符串处理、模拟、哈希表、引号里面有空格的字符串怎么读入
题目分析 来源:acwing 分析: 本题采用vector< string > 来读入原来模板.接下来的m行需要用到哈希表,进行模板和具体内容的映射. 遍历vector,如果找到{{,就对 ...
- C++ 使用哈希表封装模拟实现unordered_map unordered_set
一.unordered_map unordered_set 和 map set的区别 1. map set底层采取的红黑树的结构,unordered_xxx 底层数据结构是哈希表.unordered_ ...
- JavaScript数据结构与算法(2)(集合、字典、哈希表、二叉树、图)(ES6)
注意:原教学视频:JavaScript(ES6)数据结构和算法 | JavaScript数据结构与算法 (都是CoderWhy老师的教学) 原作者(笔记)链接:JavaScript 数据结构与算法 | ...
最新文章
- PHP如何更好的利用PHPstorm的自动提示
- windows7 下 安装ipython-5.1.0报错
- linux 下使用 curl 访问带多参数,GET掉参数解决方案
- SpringMVC 4.0常用注解
- java 代码效率_Java效率
- STM32下一次程序后J-link不能识别问题解决
- 2020年周记(1/50)
- pytorch梯度下降函数_Pytorch中常用的四种优化器SGD、Momentum、RMSProp、Adam
- 图像算法八:【图像分割】边缘检测(Roberts,Sobel,canny)、霍夫变换(hough)、阈值分割、区域分割
- C++学习笔记(14) static_cast 与 dynamic_cast
- WinEdt 参考文献格式
- maya python插件_使用Python开发Maya导出插件的一些辅助
- 今天你又丧了吗? | Alfred数据室
- 程序员如何搭建自己的知识体系?
- iOS之地图的使用和实时描绘运动轨迹
- 日常pytho3练习脚本之--两个逗比聊天机器人
- 【网页制作】CSS文本和字体属性讲解【附讲解视频】
- linux 守护进程小结
- js实现小球抛物线运动
- leetcode974. 和可被 K 整除的子数组
热门文章
- 手机上如何将图片转换成PDF文档
- python统计次数正则_Python提取信息必学基础——正则表达式
- lvgl v8 line_meter
- 世界是由什么组成的java_世界是由什么组成的?
- 微软服务器系统ssd,免费试用微软2个月Windows 365云桌面,4核心+16G内存+128SSD
- 场景:一个年级,相当链表A ,该年级5个班,每个班5个人,相当于链表B1--B5:做一个学生成绩管理系统学生成绩有语文 数学 英语功能: 录入成绩 找三科总分的最高分 最低分 算出平均分
- 算法补完计划(五) 二分图匹配
- 带你走进程序猿的内心世界
- Re: 征男友征男友(有女友者勿回) (转载)
- 阿里放弃SpringCloud、Dubbo,选择的这个神仙框架!牛逼