【STL】12.unordered_set与unordered_map的模拟实现

一、源码及框架分析

SGI-STL30版本源代码中没有unordered_map和unordered_set,SGI-STL30版本是C++11之前的STL版本,这两个容器是C++11之后才更新的。但是SGI-STL30实现了哈希表,只容器的名字是hash_map和hash_set,他是作为非标准的容器出现的,非标准是指非C++标准规定必须实现的,源代码在hash_map /hash_set / stl_hash_map / stl_hash_set / stl_hashtable.h 中,hash_map和hash_set的实现结构框架核心部分截取出来如下:

// stl_hash_set
template <class Value, class HashFcn = hash<Value>,class EqualKey = equal_to<Value>,class Alloc = alloc>
class hash_set
{
private:typedef hashtable<Value, Value, HashFcn, identity<Value>, EqualKey, Alloc> ht;ht rep;
public:typedef typename ht::key_type key_type;typedef typename ht::value_type value_type;typedef typename ht::hasher hasher;typedef typename ht::key_equal key_equal;typedef typename ht::const_iterator iterator;typedef typename ht::const_iterator const_iterator;hasher hash_funct() const { return rep.hash_funct(); }key_equal key_eq() const { return rep.key_eq(); }
};
// stl_hash_map
template <class Key, class T, class HashFcn = hash<Key>,class EqualKey = equal_to<Key>, class Alloc = alloc>
class hash_map
{
private:typedef hashtable<pair<const Key, T>, Key, HashFcn,select1st<pair<const Key, T> >, EqualKey, Alloc> ht;ht rep;
public:typedef typename ht::key_type key_type;typedef T data_type;typedef T mapped_type;typedef typename ht::value_type value_type;typedef typename ht::hasher hasher;typedef typename ht::key_equal key_equal;typedef typename ht::iterator iterator;typedef typename ht::const_iterator const_iterator;
};// stl_hashtable.h
template <class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, class Alloc>
class hashtable {
public:typedef Key key_type;typedef Value value_type;typedef HashFcn hasher;typedef EqualKey key_equal;
private:hasher hash;key_equal equals;ExtractKey get_key;typedef __hashtable_node<Value> node;vector<node*,Alloc> buckets;size_type num_elements;
public:typedef __hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc> iterator;pair<iterator, bool> insert_unique(const value_type& obj);const_iterator find(const key_type& key) const;
};
template <class Value>
struct __hashtable_node
{__hashtable_node* next;Value val;
};

• 这里我们就不再画图分析了,通过源码可以看到,结构上hash_map和hash_set跟map和set的完全类似,复用同一个hashtable实现key和key/value结构,hash_set传给hash_table的是两个key,hash_map传给hash_table的是pair<const key, value>。
• 需要注意的是源码里面跟map/set源码类似,命名风格比较乱,这里比map和set还乱,下面我们模拟一份自己的出来,就按自己的风格走了。

二、模拟实现unordered_set 和unordered_map

2.1 实现出符合要求的哈希表

2.1.1 iterator的实现

iterator实现思路分析
• iterator实现的大框架跟list的iterator思路是一致的,用一个类型封装结点的指针,再通过重载运算符实现,迭代器像指针一样访问的行为,要注意的是哈希表的迭代器是单向迭代器。
• 这里的难点是operator++的实现。iterator中有一个指向结点的指针,如果当前桶下面还有结点,则结点的指针指向下一个结点即可。如果当前桶走完了,则需要想办法计算找到下一个桶。这里的难点是反而是结构设计的问题,我们想到iterator中除了有结点的指针,还有哈希表对象的指针,这样当前桶走完了,要计算下一个桶就相对容易多了,用key值计算出当前桶位置,依次往后找下一个不为空的桶即可。
• begin()返回第一个桶中第一个节点指针构造的迭代器,这里end()返回迭代器可以用空表示。
• unordered_set的iterator也不支持修改,我们把unordered_set的第二个模板参数改成const K即可, HashTable<K, const K, SetKeyOfT, Hash> _ht;
• unordered_map的iterator不支持修改key但是可以修改value,我们把unordered_map的第二个模板参数pair的第一个参数改成const K即可, HashTable<K, pair<const K, V>,MapKeyOfT, Hash> _ht;

2.2.2 改造后的哈希表代码

#pragma once#include<iostream>
#include<vector>
#include<cassert>
#include<algorithm>
using namespace std;//状态
enum State
{Empty,Delete,Exit
};//哈希表中的数据
template<class T>
struct HashNode
{T _t;HashNode* next;HashNode(const T& t) :_t(t), next(nullptr) {}
};template<class K, class T, class Ref,class Ptr,class KeyOfT, class Hash>
struct HashIterator
{typedef HashNode<T> Node;typedef HashIterator self;Node* _node;vector<Node*> _ht;HashIterator(Node* node,const vector<Node*>&ht):_node(node),_ht(ht){}self& operator++(){//有下一个节点if (_node->next){_node = _node->next;}else{size_t hashi = Hash()(KeyOfT()(_node->_t)) % _ht.size();hashi++;while (hashi < _ht.size()){if (_ht[hashi])	break;hashi++;}if (hashi == _ht.size())_node = nullptr;else_node = _ht[hashi];}return *this;}self operator++(int){self temp(_node);++_node;return temp;}Ptr operator->(){return &_node->_t;}Ref operator*(){return _node->_t;}bool operator!=(const HashIterator& ht){return _node != ht._node;}bool operator==(const HashIterator& ht){return _node == ht._node;}
};template<class K, class T,class KeyOfT, class Hash>
class HashTable
{typedef HashNode<T> Node;
public://迭代器typedef HashIterator<K, T, T&,T*,KeyOfT, Hash> iterator;typedef HashIterator<K, T, const T&,const T*,KeyOfT, Hash> const_iterator;iterator begin(){if(_n==0)return end();size_t i = 0;while ( i<_ht.size()){if(_ht[i])return  iterator(_ht[i], _ht);i++;}return end();}const_iterator begin()const{if (_n == 0)return end();size_t i = 0;while (i < _ht.size()){if (_ht[i])return  iterator(_ht[i], _ht);i++;}return end();}iterator end(){return iterator(nullptr, _ht);}const_iterator end()const{return iterator(nullptr, _ht);}HashTable(){_ht.resize(10);_n = 0;}//查找iterator find(const K& key){size_t hashi = Hash()(key) % _ht.size();//定位Node* cur = _ht[hashi];while (cur){if (KeyOfT()(cur->_t) == key)return iterator(cur,_ht);cur = cur->next;}return iterator(nullptr, _ht);}pair<iterator,bool> insert(const T& t){iterator ret = find(KeyOfT()(t));if (ret._node)return {ret,false };//扩容if (_n == _ht.size()){vector<Node*> newht(_ht.size() * 2,nullptr);for (int i = 0; i < _ht.size(); i++){Node* cur = _ht[i];while (cur){Node* next = cur->next;size_t hashi = Hash()(KeyOfT()(_ht[i]->_t)) % newht.size();if (newht[i] == nullptr)newht[i] = cur;else{cur->next = newht[i];newht[i] = cur;}_ht[i] = next;}}_ht.swap(newht);}size_t hashi = Hash()(KeyOfT()(t)) % _ht.size();//定位Node* newnode = new Node(t);newnode->next = _ht[hashi];_ht[hashi] = newnode;_n++;return { {newnode,_ht},true };}iterator erase(const K& key){size_t hashi = Hash()(key) % _ht.size();//定位Node* prev = nullptr;Node* cur = _ht[hashi];Node* ret = nullptr;while (cur){if (KeyOfT()(cur->_t) == key){Node* temp = cur;ret = ++temp;if (prev == nullptr){					_ht[hashi] = nullptr;}else{prev->next = cur->next;}delete cur;cur = nullptr;_n--;return{ ret, _ht };}prev = cur;cur = cur->next;}return { nullptr,_ht };}
private:vector<Node*> _ht;size_t _n;
};

2.2 复用哈希表实现unordered_set

#pragma once#include"HashBucket.h"
//对于int 、double、size_t 、int* 等类型
template<class K>
struct HashFunc_set
{size_t operator()(const K& key){return size_t(key);}
};//对于string 的特化处理
template<>
struct HashFunc_set<string>
{size_t operator()(const string& key){size_t ret = 0;for (const auto& e : key)ret = ret * 31 + e;return ret;}
};template<class K,class Hash = HashFunc_set<K>>
class unordered_set
{typedef K T;//和map相称struct KeyOfT{const K& operator()(const T& t){return t;}};typedef typename HashTable<K, T, KeyOfT, Hash>::iterator iterator;typedef typename HashTable<K, T, KeyOfT, Hash>::const_iterator const_iterator;
public:iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}const_iterator begin()const{return _ht.begin();}const_iterator end()const{return _ht.end();}iterator find(const K& key){return _ht.find(key);}pair<iterator, bool> insert(const T& t){return _ht.insert(t);}iterator erase(const K& key){return _ht.erase(key);}
private:HashTable<K, T, KeyOfT, Hash> _ht;
};

2.3 复用哈希表实现unordered_map

#pragma once#include"HashBucket.h"
//对于int 、double、size_t 、int* 等类型
template<class K>
struct HashFunc_map
{size_t operator()(const K& key){return size_t(key);}
};//对于string 的特化处理
template<>
struct HashFunc_map<string>
{size_t operator()(const string& key){size_t ret = 0;for (const auto& e : key)ret = ret * 31 + e;return ret;}
};template<class K,class V,class Hash= HashFunc_map<K>>
class unordered_map
{typedef pair<K, V> T;//和map相称struct KeyOfT{const K& operator()(const T& t){return t.first;}};typedef typename HashTable<K, T,KeyOfT, Hash>::iterator iterator;typedef typename HashTable<K, T,KeyOfT, Hash>::const_iterator const_iterator;
public:iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}const_iterator begin()const{return _ht.begin();}const_iterator end()const{return _ht.end();}iterator find(const K& key){return _ht.find(key);}pair<iterator, bool> insert(const T& t){return _ht.insert(t);}iterator erase(const K& key){return _ht.erase(key);}V& operator[](const K& key){pair<iterator, bool> ret = insert({ key,V() });return ret.first->second;}
private:HashTable<K, T,KeyOfT,Hash> _ht;
};

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

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

相关文章

macOS 的目录结构

文章目录 根目录 (/)常见目录及其用途示例目录结构注意事项根目录 (/)主要目录及其含义其他目录总结 macOS 的目录结构无论是在 Intel 架构还是 ARM 架构的 Mac 电脑上都是相同的。macOS 的目录结构遵循 Unix 和 BSD 的传统&#xff0c;具有许多标准目录。以下是一些主要目录及…

003 STM32基础、架构以及资料介绍——常识

注&#xff1a; 本笔记参考学习B站官方视频教程&#xff0c;免费公开交流&#xff0c;切莫商用。内容可能有误&#xff0c;具体以官方为准&#xff0c;也欢迎大家指出问题所在。 01什么是STM32&#xff08;宏观&#xff09; STM32属于一个微控制器&#xff0c;自带了各种常用通…

aws凭证(一)凭证存储

AWS 凭证用于验证身份&#xff0c;并授权对 DynamoDB 等等 AWS 服务的访问。配置了aws凭证后&#xff0c;才可以通过编程方式或从AWS CLI连接访问AWS资源。凭证存储在哪里呢&#xff1f;有以下几个方法&#xff1a; 一、使用文件存储 1、介绍 文件存储适用于长期和多账户配置…

力扣面试经典 150(上)

文章目录 数组/字符串1. 合并两个有序数组2. 移除元素3. 删除有序数组中的重复项4. 删除有序数组的重复项II5. 多数元素6. 轮转数组7. 买卖股票的最佳时机8. 买卖股票的最佳时机II9. 跳跃游戏10. 跳跃游戏II11. H 指数12. O(1)时间插入、删除和获取随机元素13. 除自身以外数组的…

聚焦AI存储,联想凌拓全力奔赴

【全球存储观察 &#xff5c; 科技热点关注】 每一个时代&#xff0c;都有每一个时代的骄傲。 在信息化时代&#xff0c;NAS文件存储肩负着非结构化数据管理与存储的重任&#xff0c;NetApp以其创新实力&#xff0c;赢得了全球存储市场的极高声誉。 在数智化时代&#xff0c;…

JavaWeb后端开发知识储备2

目录 1.HttpClient 2.微信小程序开发 3.Spring Cache 1.HttpClient 简单来说&#xff0c;HttpClient可以通过编码的方式在Java中发送Http请求 2.微信小程序开发 微信小程序的开发本质上是前端开发&#xff0c;对于后端程序员来说了解即可 3.Spring Cache Spring Cache 是…

基于CNN+RNNs(LSTM, GRU)的红点位置检测(pytorch)

1 项目背景 需要在图片精确识别三跟红线所在的位置&#xff0c;并输出这三个像素的位置。 其中&#xff0c;每跟红线占据不止一个像素&#xff0c;并且像素颜色也并不是饱和度和亮度极高的红黑配色&#xff0c;每个红线放大后可能是这样的。 而我们的目标是精确输出每个红点的…

树莓派搭建NextCloud:给数据一个安全的家

前言 NAS有很多方案&#xff0c;常见的有 Nextcloud、Seafile、iStoreOS、Synology、ownCloud 和 OpenMediaVault &#xff0c;以下是他们的特点&#xff1a; 1. Nextcloud 优势&#xff1a; 功能全面&#xff1a;支持文件同步、共享、在线文档编辑、视频会议、日历、联系人…

数据集-目标检测系列- 花卉 鸡蛋花 检测数据集 frangipani >> DataBall

数据集-目标检测系列- 花卉 鸡蛋花 检测数据集 frangipani >> DataBall DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 贵在坚持&#xff01; 数据样例项目地址&#xff1a; * 相关项目 1&#xff09;数据集…

初次体验加猜测信息安全管理与评估国赛阶段训练习

[第一部分] 网络安全事件响应 window操作系统服务器应急响应流程_windows 服务器应急响应靶场_云无迹的博客-CSDN博客 0、请提交攻击者攻击成功的第一时间&#xff0c;格式&#xff1a;YY:MM:DD hh:mm:ss1、请提交攻击者的浏览器版本2、请提交攻击者目录扫描所使用的工具名称…

Python Matplotlib 安装指南:使用 Miniconda 实现跨 Linux、macOS 和 Windows 平台安装

Python Matplotlib 安装指南&#xff1a;使用 Miniconda 实现跨 Linux、macOS 和 Windows 平台安装 Matplotlib是Python最常用的数据可视化工具之一&#xff0c;结合Miniconda可以轻松管理安装和依赖项。在这篇文章中&#xff0c;我们将详细介绍如何使用Miniconda在Linux、mac…

opencv-python 分离边缘粘连的物体(距离变换)

import cv2 import numpy as np# 读取图像&#xff0c;这里添加了判断图像是否读取成功的逻辑 img cv2.imread("./640.png") # 灰度图 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 高斯模糊 gray cv2.GaussianBlur(gray, (5, 5), 0) # 二值化 ret, binary cv2…

KubeSphere内网环境实践GO项目流水线

KubeSphere内网环境实践GO项目流水线 kubesphere官方给出的流水线都是在公网环境下&#xff0c;并对接github、dockerhub等环境。本文在内网实践部署&#xff0c;代码库使用内网部署的gitlab&#xff0c;镜像仓库使用harbor。 1. 环境准备 1.1 部署kubesphere环境 参考官方…

UE5材质篇5 简易水面

不得不说&#xff0c;UE5里搞一个水面实在是相比要自己写各种反射来说太友好了&#xff0c;就主要是开启一堆开关&#xff0c;lumen相关的&#xff0c;然后稍微连一些蓝图就几乎有了 这里要改一个shading model&#xff0c;要这个 然后要增加一个这个node 并且不需要连接base …

浦语提示词工程实践(LangGPT版,服务器上部署internlm2-chat-1_8b,踩坑很多才完成的详细教程,)

首先&#xff0c;在InternStudio平台上创建开发机。 创建成功后点击进入开发机打开WebIDE。进入后在WebIDE的左上角有三个logo&#xff0c;依次表示JupyterLab、Terminal和Code Server&#xff0c;我们使用Terminal就行。&#xff08;JupyterLab可以直接看文件夹&#xff09;…

小白学多线程(持续更新中)

1.JDK中的线程池 JDK中创建线程池有一个最全的构造方法&#xff0c;里面七个参数如上所示。 执行流程分析&#xff1a; 模拟条件&#xff1a;10个核心线程数&#xff0c;200个最大线程数&#xff0c;阻塞队列大小为100。 当有小于十个任务要处理时&#xff0c;因为小于核心线…

40分钟学 Go 语言高并发:Context包与并发控制

Context包与并发控制 学习目标 知识点掌握程度应用场景context原理深入理解实现机制并发控制和请求链路追踪超时控制掌握超时设置和处理API请求超时、任务限时控制取消信号传播理解取消机制和传播链优雅退出、资源释放context最佳实践掌握使用规范和技巧工程实践中的常见场景…

音频信号采集前端电路分析

音频信号采集前端电路 一、实验要求 要求设计一个声音采集系统 信号幅度&#xff1a;0.1mVpp到1Vpp 信号频率&#xff1a;100Hz到16KHz 搭建一个带通滤波器&#xff0c;滤除高频和低频部分 ADC采用套件中的AD7920&#xff0c;转换率设定为96Ksps &#xff1b;96*161536 …

SpringBoot中使用Sharding-JDBC实战(实战+版本兼容+Bug解决)

一、实战 1、引入 ShardingSphere-JDBC 的依赖 https://mvnrepository.com/artifact/org.apache.shardingsphere/shardingsphere-jdbc/5.5.0 <!-- https://mvnrepository.com/artifact/org.apache.shardingsphere/shardingsphere-jdbc --> <dependency><grou…

网络编程 day1.2~day2——TCP和UDP的通信基础(TCP)

笔记脑图 作业&#xff1a; 1、将虚拟机调整到桥接模式联网。 2、TCP客户端服务器实现一遍。 服务器 #include <stdio.h> #include <string.h> #include <myhead.h> #define IP "192.168.60.44" #define PORT 6666 #define BACKLOG 20 int mai…