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");
//}
谢谢大家!
相关文章:
C/C++进阶 (8)哈希表(STL)
个人主页:仍有未知等待探索-CSDN博客 专题分栏:C 本文着重于模拟实现哈希表,并非是哈希表的使用。 实现的哈希表的底层用的是线性探测法,并非是哈希桶。 目录 一、标准库中的哈希表 1、unordered_map 2、unordered_set 二、模…...
2024电赛H题参考方案(+视频演示+核心控制代码)——自动行驶小车
目录 一、题目要求 二、参考资源获取 三、TI板子可能用到的资源 1、环境搭建及工程移植 2、相关模块的移植 四、控制参考方案 1、整体控制方案视频演示 2、视频演示部分核心代码 五、总结 一、题目要求 小编自认为:此次控制类类型题目的H题,相较于往年较…...
设计模式14-享元模式
设计模式14-享元模式 由来动机定义与结构代码推导特点享元模式的应用总结优点缺点使用享元模式的注意事项 由来动机 在很多应用中,可能会创建大量相似对象,例如在文字处理器中每个字符对象。在这些场景下,如果每个对象都独立存在,…...
Javascript中canvas与svg详解
Canvas 在JavaScript中,<canvas> 元素用于在网页上绘制图形,如线条、圆形、矩形、图像等。它是一个通过JavaScript和HTML的<canvas>元素来工作的绘图表面。<canvas> 元素自身并不具备绘图能力,它仅仅提供了一个绘图环境&a…...
【BUG】已解决:No Python at ‘C:Users…Python Python39python. exe’
No Python at ‘C:Users…Python Python39python. exe’ 目录 No Python at ‘C:Users…Python Python39python. exe’ 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页,我是博主英杰,211科班…...
Flink SQL 的工作机制
前言 Flink SQL 引擎的工作流总结如图所示。 从图中可以看出,一段查询 SQL / 使用TableAPI 编写的程序(以下简称 TableAPI 代码)从输入到编译为可执行的 JobGraph 主要经历如下几个阶段: 将 SQL文本 / TableAPI 代码转化为逻辑执…...
[AI Mem0] 源码解读,带你了解 Mem0 的实现
Mem0 的 CRUD 到底是如何实现的?我们来看下源码。 使用 先来看下,如何使用 Mem0 import os os.environ["OPENAI_API_KEY"] "sk-xxx"from mem0 import Memorym Memory()# 1. Add: Store a memory from any unstructured text re…...
【LLM】-10-部署llama-3-chinese-8b-instruct-v3 大模型
目录 1、模型下载 2、下载项目代码 3、启动模型 4、模型调用 4.1、completion接口 4.2、聊天(chat completion) 4.3、多轮对话 4.4、文本嵌入向量 5、Java代码实现调用 由于在【LLM】-09-搭建问答系统-对输入Prompt检查-CSDN博客 关于提示词注入…...
C语言 之 理解指针(4)
文章目录 1. 字符指针变量2. 数组指针变量2.1 对数组指针变量的理解2.2 数组指针变量的初始化 3. 二维数组传参的本质4. 函数指针变量4.1 函数指针变量的创建4.2 函数指针变量的使用 5. 函数指针数组 1. 字符指针变量 我们在前面使用的主要是整形指针变量,现在要学…...
Java设计模式—单例模式(Singleton Pattern)
目录 一、定义 二、应用场景 三、具体实现 示例一 示例二 四、懒汉与饿汉 饿汉模式 懒汉模式 五、总结 六、说明 一、定义 二、应用场景 单例模式的应用场景主要包括以下几个方面: 日志系统:在应用程序中,通常只需要一个日…...
AV1帧间预测(二):运动补偿
运动补偿(Motion Compensation,MC)是帧间预测最基础的工具,AV1支持两种运动补偿方式,一种是传统的平移运动补偿,另一种是仿射运动补偿。下面分别介绍这两种运动补偿方法。 平移运动补偿 平移运动补偿是最传统的运动补偿方式,H.26…...
数学建模(5)——逻辑回归
一、二分类 import numpy as np import matplotlib.pyplot as plt from sklearn import datasets from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklea…...
【C++高阶】:深入探索C++11
✨ 心似白云常自在,意如流水任东西 🌏 📃个人主页:island1314 🔥个人专栏:C学习 🚀 欢迎关注:👍点赞 Ǵ…...
6. 自定义Docker镜像
如何自定义Docker镜像:从基础到实践 Docker作为一个容器化平台,使得应用的打包、分发和运行变得更加高效和便捷。本文将详细介绍如何自定义一个Docker镜像,包括镜像的构成、分层原理、创建自定义镜像的具体步骤,并演示如何打包和…...
「12月·长沙」人工智能与网络安全国际学术会议(ISAICS 2024)
人工智能与网络安全国际学术会议(ISAICS 2024)将于2024年12月20日-2024年12月22日在湖南长沙召开。会议中发表的文章将会被收录,并于见刊后提交EI核心索引。会议旨在在为国内与国际学者搭建交流平台,推进不同学科领域的融合发展,就当今人工智能与网络安全范畴内各学…...
【技术支持案例】使用S32K144+NSD8381驱动电子膨胀阀
文章目录 1. 前言2. 问题描述3. 理论分析3.1 NSD8381如何连接电机3.2 S32K144和NSD8381的软件配置 4.测试验证4.1 测试环境4.2 测试效果4.3 测试记录 1. 前言 最近有客户在使用S32K144NSD8381驱动电子膨胀阀时,遇到无法正常驱动电子膨胀阀的情况。因为笔者也是刚开…...
第二期:集成电路(IC)——智能世界的微观建筑大师
嘿,小伙伴们!👋 我是你们的老朋友小竹笋,一名热爱创作和技术的工程师。上一期我们聊了聊AI芯片,这次我们要深入到更微观的层面,来探究集成电路(IC)的世界。准备好一起探索了吗&#…...
基于物联网的区块链算力网络,IGP/BGP协议
目录 基于物联网的区块链算力网络 IGP/BGP协议 IGP(内部网关协议) BGP(边界网关协议) 内部使用ISP的外部使用BGP的原因 一、网络规模和复杂性 二、路由协议的特性 三、满足业务需求 四、结论 基于物联网的区块链算力网络 通 过 多个物联网传感器将本地计算…...
每日一题~960 div2 A+B+C(简单奇偶博弈,构造,观察性质算贡献)
A题意: N 长的数组。 一次操作: 最开始的mx 为零。 选出一个数(使得这个数>mx) ,之后将mx 更新为这个数,将这个数置为零。 不能做这个操作的,输。 问是否有先手赢的策略。有的话,输出yes 否则no 当时一…...
音视频入门基础:H.264专题(17)——FFmpeg源码获取H.264裸流文件信息(视频压缩编码格式、色彩格式、视频分辨率、帧率)的总流程
音视频入门基础:H.264专题系列文章: 音视频入门基础:H.264专题(1)——H.264官方文档下载 音视频入门基础:H.264专题(2)——使用FFmpeg命令生成H.264裸流文件 音视频入门基础&…...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...
Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...
BLEU评分:机器翻译质量评估的黄金标准
BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...
DiscuzX3.5发帖json api
参考文章:PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下,适配我自己的需求 有一个站点存在多个采集站,我想通过主站拿标题,采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...


