C/C++进阶 (8)哈希表(STL)

个人主页:仍有未知等待探索-CSDN博客

专题分栏:C++

本文着重于模拟实现哈希表,并非是哈希表的使用。

实现的哈希表的底层用的是线性探测法,并非是哈希桶。

目录

一、标准库中的哈希表

1、unordered_map

2、unordered_set

二、模拟实现哈希表

1、结构

2、注解hashtable的模板参数:

3、重难点

1、二元“!=” 没有找到接收类型的右操作数的运算符​编辑

2、迭代器的本质、const和非const的区别

3、类A在类B的上面,想要在类A中使用以类B为类型的变量,怎么办?

4、类A在类B的上面,想要在类A中使用类B的私有成员,怎么办?

三、代码


一、标准库中的哈希表

这两个是两种不同映射关系的哈希表。

unordered_map:是 Key - Value 的映射关系 (也就是 关键字和键值 之间的映射)

unordered_set:是 Value 的映射关系(也就是只有 键值 的映射)

这两种不同的映射关系从各自的模板参数就能看出来。

1、unordered_map

unordered_map又分为两种,一种是unordered_map,另一种是unordered_multimap。

这两种有什么区别呢?

以unordered_map为例:

这里面的模板参数 Key - T 就是这个容器存储的键值关系。

2、unordered_set

同样:unordered_set又分为两种,一种是unordered_set,另一种是unordered_multiset。

这两个的区别和unordered_map中两个的区别一样。

二、模拟实现哈希表

1、结构

在模拟实现哈希表前,我们要知道整个实现的结构,要有完整性的认识。

这个就是哈希表的大致底层结构。

2、注解hashtable的模板参数:

  • Key:是上层结构(也就是unordered_set、unordered_map)的关键字。
  • T:是存的关键字和键值的映射。

(也就是unordered_set:pair<Key, Key>;unordered_map:pair<Key, Vaule>)

  • Ptr是T*。
  • Ref是T&。
  • KeyofT是一个仿函数,用来取T的Key。因为在这一层也不知道上一层是unordered_set、unordered_map,所以还需要传一个仿函数来进行取值。
  • hashfunc是一个仿函数,用来计算hash映射值。

对于一个整数,怎么存储到哈希表里?需要对其进行hash计算,将这个整数取模于表的大小得到。

对于一个字符串,怎么存储到哈希表里?需要对其进行hash计算,将这个字符串转换成整数,然后将这个整数取模于表的大小得到下标。

如果得到的下标冲突了,就用哈希线性探测法解决冲突。

3、重难点

写完不算难,难的是找bug。

1、二元“!=” 没有找到接收类型的右操作数的运算符

类型不匹配,你可以去你出错的位置看看该类型的模板参数的类型匹不匹配。

2、迭代器的本质、const和非const的区别

迭代器就是模拟的指针的行为。

const迭代器和非const迭代器的区别就是限制其*和->运算符的返回值。

3、类A在类B的上面,想要在类A中使用以类B为类型的变量,怎么办?

在类A的前面加上类B的前置声明即可。

4、类A在类B的上面,想要在类A中使用类B的私有成员,怎么办?

在类B中加上类A的友元。

三、代码

hash.h

#pragma once#include <iostream>
#include <vector>
#include <string>
using namespace std;
enum  State
{Empty,Exist,Delete
};
// hash_func ---> 用来对任意类型进行编码
template<class T>
struct hash_func
{size_t operator()(const T& _val){return (size_t)_val;}
};
// 对string类型进行特化
template<>
struct hash_func<string>
{size_t operator()(const string& val){size_t hash = 0;for (auto e : val){hash *= 131;hash += e;}return hash;}
};// 闭散列 --- 线性探测
// 这里的 T 是 K or pair<K, V>
template <class T>
struct HashNode
{HashNode() {}HashNode(const HashNode&) = default;HashNode(const T& data, const State& state):_data(data),_state(state){}T _data;State _state = Empty;};template <class K, class V, class Ptr, class Ref, class KeyofT, class HashFunc>
class HashTable;template<class K, class T, class Ptr, class Ref, class KeyofT>
struct HashTable_iterator
{typedef HashNode<T> HashNode;typedef HashTable_iterator<K, T, Ptr, Ref, KeyofT> iterator;typedef HashTable<K, T, Ptr, Ref, KeyofT, hash_func<K>> hashtable;HashNode* _node;hashtable* _pht;KeyofT kot;hash_func<K> hs;HashTable_iterator(HashNode* node, hashtable* pht):_node(node), _pht(pht){}bool operator!=(const iterator s){return _node != s._node;}Ref operator*(){return _node->_data;}Ptr operator->(){return &(_node->_data);}iterator& operator++(){int hashindex = 0;for (int i = 0; i < _pht->_tables.size(); i++){// 找到当前节点的位置,寻找下一个节点的位置if (_pht->_tables[i] == _node){hashindex = i;break;}}for (int i = hashindex + 1; i < _pht->_tables.size(); i++){if (_pht->_tables[i] && _pht->_tables[i]->_state == Exist){_node = _pht->_tables[i];return *this;}}_node = nullptr;return *this;}
};template <class K, class V, class Ptr, class Ref, class KeyofT, class HashFunc = hash_func<K>>
class HashTable
{typedef HashNode<V> hashnode;typedef HashTable<K, V, Ptr, Ref, KeyofT, hash_func<K>> hashtable;template<class K, class T, class Ptr, class Ref, class KeyofT>friend struct HashTable_iterator;typedef HashTable_iterator<K, V, Ptr, Ref, KeyofT> iterator;public:hash_func<K> hf;KeyofT kot;HashTable(size_t n = 10):_size(0){_tables.resize(n);}iterator begin(){for (int i = 0; i < _tables.size(); i++){hashnode* cur = _tables[i];if (cur && cur->_state == Exist){// this -> HashTable*return iterator(cur, this);}}return end();}iterator end(){return iterator(nullptr, this);}// V : V or pair<K, V>pair<iterator, bool> insert(const V& e){iterator it = find(kot(e));if (it != end()){return make_pair(it, false);}if (_size * 10 / _tables.size() >= 7){hashtable newt(_tables.size() * 2);for (int i = 0; i < _tables.size(); i ++ ){if (_tables[i]->_state == Exist){newt.insert(_tables[i]->_data);}}_tables.swap(newt._tables);}size_t hashindex = hf(kot(e)) % _tables.size();while (_tables[hashindex] && _tables[hashindex]->_state == Exist){hashindex++;hashindex %= _tables.size();}hashnode* newnode = new hashnode(e, Exist);swap(_tables[hashindex], newnode);delete newnode;_size++;return make_pair(iterator(_tables[hashindex], this), true);}iterator find(const K& key){size_t hashindex = hf(key)% _tables.size();while (_tables[hashindex] && _tables[hashindex]->_state != Empty){if (_tables[hashindex]->_state == Exist&& kot(_tables[hashindex]->_data) == key){return iterator(_tables[hashindex], this);}++hashindex;hashindex %= _tables.size();}return iterator(nullptr, this);}bool erase(const K& key){iterator t = find(key);if (t._node == nullptr) return false;else{t._node->_state = Delete;--_size;}return true;}
private:vector<hashnode*> _tables;size_t _size;
};

unordered_map.h

#pragma once#include "hash.h"template<class K, class V>
class unordered_map
{struct MapKeyofT{const K& operator()(const pair<const K, V>& key){return key.first;}};
public:typedef HashTable<K, pair<const K, V>, pair<const K, V>*, pair<const K, V>&, MapKeyofT, hash_func<K>> HashTable;typedef HashNode<pair<const K, V>> HashNode;typedef HashTable_iterator<K, pair<const K, V>, pair<const K, V>*, pair<const K, V>&, MapKeyofT> iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}pair<iterator, bool> insert(const pair<const K, V>& kv){return _ht.insert(kv);}iterator find(const K& key){return _ht.find(key);}bool erase(const K& key){return _ht.erase(key);}V& operator[](const K& key){pair<K, V> pkv = make_pair(key, V());pair<iterator, bool> ret = insert(pkv);return ret.first->second;}
private:HashTable _ht;
};//void map01()
//{
//	unordered_map<int, int> s1;
//	vector<int> v = { 1, 8, 34, 5, 3, 4, 8, 1111, 111, 11 };
//
//	for (auto e : v)
//	{
//		s1.insert({e, e});
//	}
//
//	s1.erase(1111);
//	s1.erase(8);
//}
//
//void map02()
//{
//	unordered_map<string, int> s1;
//	vector<string> v = { "1234", "233", "a", "b", "1234", "233", "a", "b" };
//
//	for (auto e : v)
//	{
//		s1.insert({e, 1});
//	}
//
//	s1.erase("1234");
//	s1.erase("8");
//}

unordered_set.h

#pragma once#include "hash.h"template<class K>
class unordered_set
{struct SetKeyofT{const K& operator()(const K& key){return key;}};
public:typedef HashTable<K, K, const K*, const K&, SetKeyofT, hash_func<K>> HashTable;typedef HashNode<K> HashNode;typedef HashTable_iterator<K, K, const K*, const K&, SetKeyofT> iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}pair<iterator, bool> insert(const K& val){return _ht.insert(val);}iterator find(const K& key){return _ht.find(key);}bool erase(const K& key){return _ht.erase(key);}
private:HashTable _ht;
};//void set01()
//{
//	unordered_set<int> s1;
//	vector<int> v = { 1, 8, 34, 5, 3, 4, 8, 1111, 11, 11};
//
//	for (auto e : v)
//	{
//		s1.insert(e);
//	}
//
//	s1.erase(1111);
//}
//void set02()
//{
//	unordered_set<string> s;
//	vector<string> v = { "1234", "233", "a", "b" };
//
//	for (auto e : v)
//	{
//		s.insert(e);
//	}
//
//	s.erase("1111");
//	s.erase("1234");
//}

谢谢大家!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/50681.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【参会邀请】第四届区块链技术与信息安全国际会议(ICBCTIS 2024)诚邀相聚江城!

我们诚挚地邀请您参与第四届区块链技术与信息安全国际会议&#xff08;ICBCTIS 2024&#xff09;。本届会议将于2024年8月17日~19日在中国武汉召开。会议将围绕区块链技术与信息安全等相关研究领域&#xff0c;特邀国内外数位在此领域学术卓越的学者专家做相关致辞与报告&#…

如何使用虚拟机如何安装 Kali Linux ?

1.下载虚拟机&#xff1a;https://www.virtualbox.org/wiki/Downloads 选择你的系统版本 2.下载kali linux系统镜像&#xff1a;https://www.kali.org/get-kali/#kali-virtual-machines 全部下载完成后&#xff0c;我们会得到以下文件&#xff01; 1.压缩Kali Linux压缩包 2.安…

Django实战:开启数字化任务管理的新纪元

&#x1f680; Django实战&#xff1a;开启数字化任务管理的新纪元 &#x1f310; &#x1f4d6; 引言 在数字化转型的浪潮中&#xff0c;任务管理的智能化成为提升组织效能的关键。今天&#xff0c;我将带领大家深入了解我们最新开发的OFTS系统——一款创新的组织任务管理软…

【Opencv】色彩空间 color space

import os import cv2 img cv2.imread(os.path.join(.,dog.jpg)) # 在opencv中使用imread,读取的图片每个像素都是bgr色彩&#xff0c;蓝色&#xff0c;绿色&#xff0c;红色 cv2.imshow(img,img) cv2.waitKey(0) # 颜色空间转化&#xff1a;BGR2RGB img_rgb cv2.cvtC…

【Python学习手册(第四版)】学习笔记10-语句编写的通用规则

个人总结难免疏漏&#xff0c;请多包涵。更多内容请查看原文。本文以及学习笔记系列仅用于个人学习、研究交流。 本文较简单&#xff0c;5-10分钟即可阅读完成。介绍Python基本过程语句并讨论整体语法模型通用规则&#xff08;冒号、省略、终止、缩进、其他特殊情况&#xff0…

【CAN通讯系列5】CAN数据帧及其仲裁

在CAN通讯系列3-CAN通讯如何传递信号中&#xff0c;由于传递信号的分析需要&#xff0c;引出了CAN数据帧的ID&#xff0c;长度和数据段的概念&#xff0c;它们都与CAN协议帧相关。CAN协议帧有5种类型&#xff0c;如下表&#xff1a; 而我们当前使用到的是数据帧&#xff0c;故本…

正向解析、反向解析、DNS主从、多区域、ntp时间同步

DNS配置回顾 编号主机名IP地址说明1web服务器192.168.1.17发布部署web服务2dns服务器192.168.1.20用于解析域名和IP地址3clien主机192.168.1.18用于模拟客户机 修改 client主机&#xff1a;修改了dns的访问主机&#xff1b;临时修改echo "nameserver IP地址"&…

【Web开发手礼】探索Web开发的秘密(十三)-Vue(3)好友列表、登录

前言 主要介绍了好友列表、登录界面所涉及的vue知识点&#xff01;&#xff01;&#xff01; 好友列表 从云端API读取数据信息 地址 https://app165.acapp.acwing.com.cn/myspace/userlist/方法&#xff1a;GET是否验证jwt&#xff1a;否输入参数&#xff1a;无返回结果&…

基于okhttp3拦截器实现短时间内重复请求的拦截

基于okhttp3拦截器实现短时间内重复请求的拦截 背景 某次需求代码实现存在缺陷, 导致用户在点击某标签的时候发起了2次请求(即一次重复请求)。由于开发自测阶段没有盯着抓包软件看请求次数, 测试也没有关注接口请求次数问题, 最终将问题带上线。 影响面 导致被调用的接口QPS翻…

C#知识|文件与目录操作:文本读写操作

哈喽,你好啊,我是雷工! 今天学习文件与目录的操作,以下为文本读写操作的学习笔记。 01 文件操作说明 1.1、数据的存取方式 数据库:适合存取大量且关系复杂并有序的数据; 文件:适合存取大量但数据关系简单的数据,像系统的日志文件; 1.2、文件存取的优点 ①:读取操…

ECharts - 坐标轴刻度数值处理

写图表时&#xff0c;Y轴的数值过大&#xff0c;不太可能直接展示&#xff0c;这时候就得简写了&#xff0c;或者百分比展示的也要处理&#xff0c;如下图&#xff1a; yAxis: {type: value,// Y轴轴线axisLine: { show: false }, // 刻度线axisTick: { show: false },// 轴刻度…

收藏!2024年GPU算力最新排名

​GPU&#xff08;图形处理单元&#xff09;算力的提升是驱动当代科技革命的核心力量之一&#xff0c;尤其在人工智能、深度学习、科学计算和超级计算机领域展现出了前所未有的影响力。2024年的GPU技术发展&#xff0c;不仅体现在游戏和图形处理的传统优势上&#xff0c;更在跨…

House of Lore

House of Lore 概述&#xff1a; House of Lore 攻击与 Glibc 堆管理中的 Small Bin 的机制紧密相关。House of Lore 可以实现分配任意指定位置的 chunk&#xff0c;从而修改任意地址的内存。House of Lore 利用的前提是需要控制 Small Bin Chunk 的 bk 指针&#xff0c;并且…

Android中如何手动制造logcat各等级日志(VERBOSE、DEBUG、INFO、WARNING、ERROR、FATAL)

文章目录 1、logcat与log工具2、通过log生成logcat日志2.1、logcat日志等级2.2、log指令说明2.3、log生成日志指令 3、制作日志生成shell脚本4、增加日志生成控制5、附录 1、logcat与log工具 logcat&#xff1a;是Android操作系统中用于记录和查看系统日志的工具。它是Android…

如何在 VPS 上安装和使用 VirtualMin

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 关于 Virtualmin Virtualmin 是 Webmin 的一个模块&#xff0c;允许对&#xff08;多个&#xff09;虚拟专用服务器进行广泛的管理。您…

【华为OD机考】2024D卷最全真题【完全原创题解 | 详细考点分类 | 不断更新题目】

可上 欧弟OJ系统 练习华子OD、大厂真题 绿色聊天软件戳 od1441了解算法冲刺训练&#xff08;备注【CSDN】否则不通过&#xff09; 文章目录 相关推荐阅读栈常规栈单调栈 队列&#xff08;题目极少&#xff0c;几乎不考&#xff09;哈希哈希集合哈希表 前缀和双指针同向双指针 贪…

七、SpringBoot日志

1. 得到日志对象 import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; //打印日志…

C++程序使用开源zlib库对二进制字节流数据进行压缩和解压(附源码)

目录 1、概述 2、zlib开源库与开源zip.cpp和unzip.cpp的区别 3、发送端先调用compress压缩,再将数据发出去 4、接收端接收到数据,调用uncompress解压,解压后再使用 5、最后 C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)https://blog.c…

c++-封装案例-设计学生类

类中的属性和行为统称为成员&#xff0c;属性&#xff1a;成员属性、成员变量&#xff1b;行为&#xff1a;成员函数&#xff0c;成员方法。