c++哈希(哈希表闭散列线性探测实现)
创始人
2024-04-05 14:11:26

文章目录

  • 0. 前言
  • 1. 线性探测
  • 2. 线性探测的代码实现
    • 2.0 定义
    • 2.1 插入实现--Insert
    • 2.2 查找实现--Find
    • 2.3 删除实现--Erase
    • 2.4 仿函数
  • 3. 完整代码实现
  • 4. 代码测试并运行结果:

0. 前言

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

1. 线性探测

  • 线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。
  • 插入
    • 通过哈希函数获取待插入元素在哈希表中的位置。
    • 如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突,使用线性探测找到下一个空位置,插入新元素。
  • 删除
    采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他元素的搜索。比如删除元素4,如果直接删除掉,44查找起来可能会受影响。因此线性探测采用标记的伪删除法来删除一个元素。
    我们需要借助枚举函数来完成
	enum State{EMPTY,	//空标记EXIST,	//存在DELETE	//删除};

2. 线性探测的代码实现

2.0 定义

	enum State{EMPTY,	//空标记EXIST,	//存在DELETE	//删除};templatestruct HashDate{pair _kv;State _state = EMPTY;};templateclass HashTable{public:// 插入bool Insert(const pair& kv);// 查找HashDate* Find(const K& key);// 删除bool Erase(const K& key);size_t Size()const{return _size;}bool Empty() const{return _size == 0;}private:vector _tables;size_t _size = 0;		// 存储有效数据个数};

2.1 插入实现–Insert

思路::hash(key) = key % capacity;我们根据取模来确定映射的相对位置。
注意:扩容的方法我们采用负载因子的方法
在这里插入图片描述
只要负载因子到0.7,我们就扩容;扩容后我们又要重新线性探索太麻烦了,我们可以复用一下Insert;把线性探索任务交给在栈上开辟的类实例化的新对象去作插入重新映射;然后再进行交换即可。具体看下面代码:

	bool Insert(const pair& kv){if (Find(kv.first)){return false;}//负载因子到了就扩容if (_tables.size() == 0 || 10 * _size / _tables.size() >= 7)  //扩容{size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;HashTable newHT;newHT._tables.resize(newSize);//旧表数据映射到新表内for (auto e : _tables){if (e._state == EXIST){newHT.Insert(e._kv);}}_tables.swap(newHT._tables);}size_t hashi = kv.first % _tables.size();//线性探测while (_tables[hashi]._state == EXIST){++hashi;hashi %= _tables.size();}//找到位置了_tables[hashi]._kv = kv;_tables[hashi]._state = EXIST;++_size;return true;}

2.2 查找实现–Find

根据负载因子扩容;我们知道一定有一部分的位置是空也就是被标记为EMPTY。
思路:根据映射关系取查找值的模,找到相对位置;然后往后一次查找知道遇到EMPTY标记的位置。

// 查找HashDate* Find(const K& key){if (_tables.size()==0){return nullptr;}size_t hashi =key % _tables.size();while (_tables[hashi]._state != EMPTY){if (_tables[hashi]._state != DELETE && key == _tables[hashi]._kv.first){return  &_tables[hashi];}++hashi;hashi %= _tables.size();}//没找到return nullptr;}

2.3 删除实现–Erase

我们这里的删除是标记型删除。

// 删除bool Erase(const K& key){HashDate* ret = Find(key);if (ret){ret->_state = DELETE;--_size;return true;}else{return false;}}

2.4 仿函数

  • 我们发现我们现在实现的哈希只能存数字,字符串等不行;这个时候我们需要借助仿函数。
	templatestruct HashFunc{size_t operator()(const k& key){return (size_t)key;}};//特化--stringtemplate<>struct HashFunc{size_t operator()(const string& s){size_t val = 0;for (const auto ch : s)	//迭代器{val *= 131;val += ch;}return val;}};

可能大家都很疑惑,string所有字母ASCII值相加理解用来比较;为什么乘于131,大佬经过反复推敲保证它们值稳定,就是下次映射的时候还是原来的数值。
在这里插入图片描述

3. 完整代码实现

#pragma oncenamespace CloseHash
{enum State{EMPTY,	//空标记EXIST,	//存在DELETE	//删除};templatestruct HashDate{pair _kv;State _state = EMPTY;};templatestruct HashFunc{size_t operator()(const k& key){return (size_t)key;}};//特化--stringtemplate<>struct HashFunc{size_t operator()(const string& s){size_t val = 0;for (const auto ch : s)	//迭代器{val *= 131;val += ch;}return val;}};template>class HashTable{public:bool Insert(const pair& kv){if (Find(kv.first)){return false;}//负载因子到了就扩容if (_tables.size() == 0 || 10 * _size / _tables.size() >= 7)  //扩容{size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;HashTable newHT;newHT._tables.resize(newSize);//旧表数据映射到新表内for (auto e : _tables){if (e._state == EXIST){newHT.Insert(e._kv);}}_tables.swap(newHT._tables);}Hash hash;size_t hashi = hash(kv.first) % _tables.size();//线性探测while (_tables[hashi]._state == EXIST){++hashi;hashi %= _tables.size();}//找到位置了_tables[hashi]._kv = kv;_tables[hashi]._state = EXIST;++_size;return true;}// 查找HashDate* Find(const K& key){if (Empty()){return nullptr;}Hash hash;size_t hashi = hash(key) % _tables.size();while (_tables[hashi]._state != EMPTY){if (_tables[hashi]._state != DELETE && key == _tables[hashi]._kv.first){return  &_tables[hashi];}++hashi;hashi %= _tables.size();}//没找到return nullptr;}// 删除bool Erase(const K& key){HashDate* ret = Find(key);if (ret){ret->_state = DELETE;--_size;return true;}else{return false;}}bool Empty() const{return _size == 0;}size_t Size()const{return _size;}//打印一下,用来测试void Print(){for (size_t i = 0; i < _tables.size(); ++i){if (_tables[i]._state == EXIST){printf("[%d:%d] ", i, _tables[i]._kv.first);}else{printf("[%d:*] ", i);}}cout << endl;}private:vector> _tables;size_t _size = 0;	//存储多少个有效数据};void TestHT1(){//int a[] = { 1, 11, 4, 15, 26, 7, 44, 9 };int a[] = { 1, 11, 4, 15, 26, 7, 44 };HashTable ht;for (auto e : a){ht.Insert(make_pair(e, e));}ht.Print();ht.Erase(4);cout << ht.Find(44)->_kv.first << endl;cout << ht.Find(4) << endl;ht.Print();ht.Insert(make_pair(-2, -2));ht.Print();cout << ht.Find(-2)->_kv.first << endl;}void TestHT2(){string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };//HashTable countHT;HashTable countHT;for (auto& str : arr){auto ptr = countHT.Find(str);if (ptr){ptr->_kv.second++;}else{countHT.Insert(make_pair(str, 1));}}for (auto& str : arr){cout << str << ":" << countHT.Find(str)->_kv.second << "---";}cout << endl;}void TestHT3(){HashFunc hash;cout << hash("abcd") << endl;cout << hash("bcad") << endl;cout << hash("eat") << endl;cout << hash("ate") << endl;cout << hash("abcd") << endl;cout << hash("aadd") << endl << endl;cout << hash("abcd") << endl;cout << hash("bcad") << endl;cout << hash("eat") << endl;cout << hash("ate") << endl;cout << hash("abcd") << endl;cout << hash("aadd") << endl << endl;}
}

4. 代码测试并运行结果:

在这里插入图片描述

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...