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

news2024/11/25 0:52:57

一、源码及框架分析

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.coloradmin.cn/o/2246984.html

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

相关文章

Apple Vision Pro开发002-新建项目配置

一、新建项目 可以选择默认的&#xff0c;也可以选择Universal 3D 二、切换打包平台 注意选择Target SDK为Devices SDk&#xff0c;这种适配打包到真机调试 三、升级新的Input系统 打开ProjectSettings&#xff0c;替换完毕之后引擎会重启 四、导入PolySpatial 修改上图红…

【StarRocks】starrocks 3.2.12 【share-nothing】 多Be集群容器化部署

文章目录 一. 集群规划二.docker compose以及启动脚本卷映射对于网络环境变量 三. 集群测试用户新建、赋权、库表初始化断电重启扩容 BE 集群 一. 集群规划 部署文档 https://docs.starrocks.io/zh/docs/2.5/deployment/plan_cluster/ 分类描述FE节点1. 主要负责元数据管理、…

LLaMA-Factory 上手即用教程

LLaMA-Factory 是一个高效的大型语言模型微调工具&#xff0c;支持多种模型和训练方法&#xff0c;包括预训练、监督微调、强化学习等&#xff0c;同时提供量化技术和实验监控&#xff0c;旨在提高训练速度和模型性能。 官方开源地址&#xff1a;https://github.com/hiyouga/L…

Java基础面试题01-请描述Java中JDK和JRE的区别?

什么是 JDK&#xff1f; JDK 全称 Java Development Kit&#xff0c;中文叫“Java 开发工具包”。 它是给 Java 开发者用的工具箱&#xff0c;里面有一切写代码、编译代码、调试代码所需要的工具。 JDK 包括什么&#xff1f; Java 编译器&#xff08;javac&#xff09;&…

Ubuntu20.04下安装向日葵

向日葵远程控制app官方下载 - 贝锐向日葵官网 下载Ununtu版的图形版本的安装deb包SunloginClient_15.2.0.63064_amd64.deb 直接执行 sudo dpkg -i SunloginClient_15.2.0.63064_amd64.deb 的话会报错: 如果在Ubuntu20.04里直接执行sudo apt install libgconf-2-4安装libgco…

Typora+PicGo+云服务器搭建博客图床

文章目录 前言一. 为什么要搭建博客图床&#xff1f;1.1 什么是图床&#xff1f;1.2 为什么要搭建博客图床? 二. 安装软件三. 配置阿里云OSS3.1 注册,开通对象储存3.2 创建bucket3.3 找到你的地域节点3.4 accessKeyId和accessKeySecret3.5 给你的阿里云账户充值 四. 配置4.1 配…

Python的3D可视化库 - vedo (2)visual子模块 基本可视化行为

文章目录 1. visual模块的继承关系2. 基类CommonVisual的方法2.1 获取对象信息2.1.1 对象本身信息2.1.2 对象的查找表2.1.3 对象标量范围2.1.4 对象缩略图 2.2 呈现对象2.2.1 在窗口显示1.2.2 对象可见性 2.2.3 对象颜色2.2.4 对象透明度 2.3 添加标度条2.3.1 2D标度条2.3.2 3D…

常用Rust日志处理工具教程

在本文中&#xff0c;我想讨论Rust中的日志。通过一些背景信息&#xff0c;我将带您了解两个日志库&#xff1a;env_logger和log4rs。最后&#xff0c;我将分享我的建议和github的片段。 Rust log介绍 log包是Rust中日志API的事实标准&#xff0c;共有五个日志级别&#xff1…

废品买卖回收管理系统|Java|SSM|Vue| 前后端分离

【重要①】前后端源码万字文档部署文档 【重要②】正版源码有问题包售后 【包含内容】 【一】项目提供非常完整的源码注释 【二】相关技术栈文档 【三】源码讲解视频 【其它服务】 【一】可以提供远程部署安装&#xff0c;包扩环境 【…

案例研究|阿特斯的JumpServer分布式部署和多组织管理实践

苏州阿特斯阳光电力科技有限公司&#xff08;以下简称为阿特斯&#xff09;是一家集太阳能光伏组件制造和为全球客户提供太阳能应用产品研发、设计、制造、销售的专业公司。 阿特斯集团总部位于加拿大&#xff0c;中国区总部位于江苏省苏州市。通过全球战略和多元化的市场布局…

tongweb安全整改

一 禁止以root账号运行tongweb服务 1 如果是首次安装须创建普通用户安装tongweb 2 如果已经使用root账号安装了tongweb 2.1 创建普通用户 2.2 使用root账号授予tongweb安装目录宿主权限为普通用户 2.3赋权成功后&#xff0c;后续启动tongweb服务必须为普通用户 二 tongRDS隐…

快速识别模型:simple_ocr,部署教程

快速识别图片中的英文、标点符号、数学符号、Emoji, 模型会输出图片中文字行的坐标位置、最低得分、识别结果。当前服务用到的模型&#xff1a;检测模型、数字识别、英文符号识别。 一、部署流程 1.更新基础环境 apt update2.安装miniconda wget https://repo.anaconda.com/…

tcpdump抓包 wireShark

TCPdump抓包工具介绍 TCPdump&#xff0c;全称dump the traffic on anetwork&#xff0c;是一个运行在linux平台可以根据使用者需求对网络上传输的数据包进行捕获的抓包工具。 tcpdump可以支持的功能: 1、在Linux平台将网络中传输的数据包全部捕获过来进行分析 2、支持网络层…

HarmonyOS4+NEXT星河版入门与项目实战(11)------Button组件

文章目录 1、控件图解2、案例实现1、代码实现2、代码解释3、运行效果4、总结1、控件图解 这里我们用一张完整的图来汇整 Button 的用法格式、属性和事件,如下所示: 按钮默认类型就是胶囊类型。 2、案例实现 这里我们实现一个根据放大和缩小按钮来改变图片大小的功能。 功…

YOLOV5 /onnx模型转换成rknn

上两篇文章讲述了pytorch模型下best.pt转换成onnx模型&#xff0c;以及将onnx进行简化成为best-sim.onnx, 接下来这篇文章讲述如何将onnx模型转换成rknn模型&#xff0c;转换成该模型是为了在rk3568上运行 1.创建share文件夹 文件夹包含以下文件best-sim.onnx,rknn-tookit2-…

【51单片机】LCD1602液晶显示屏

学习使用的开发板&#xff1a;STC89C52RC/LE52RC 编程软件&#xff1a;Keil5 烧录软件&#xff1a;stc-isp 开发板实图&#xff1a; 文章目录 LCD1602存储结构时序结构 编码 —— 显示字符、数字 LCD1602 LCD1602&#xff08;Liquid Crystal Display&#xff09;液晶显示屏是…

如何使用AWS Lambda构建一个云端工具(超详细)

首发地址&#xff08;欢迎大家访问&#xff09;&#xff1a;如何使用AWS Lambda构建一个云端工具&#xff08;超详细&#xff09; 1 前言 1.1 无服务器架构 无服务器架构&#xff08;Serverless Computing&#xff09;是一种云计算服务模型&#xff0c;它允许开发者构建和运行…

【Isaac Sim】相关问题汇总

目录 一、安装点击Install时报错二、启动时报 Failed to create any GPU devices三、加载Isaac Sim自带模型或示例时报 Isaac Sim is not responding 一、安装点击Install时报错 报错&#xff1a; request to https://asset.launcher.omniverse.nvidia.com/… failed, reason:…

Spring-02-springmvc

2. 什么是SpringMVC 2.1. 概述 Spring MVC是Spring Framework的一部分&#xff0c;是基于Java实现MVC的轻量级Web框架。 为什么要学习SpringMVC呢? Spring MVC的特点&#xff1a; 轻量级&#xff0c;简单易学高效 , 基于请求响应的MVC框架与Spring兼容性好&#xff0c;无缝…

深度学习之目标检测的技巧汇总

1 Data Augmentation 介绍一篇发表在Big Data上的数据增强相关的文献综述。 Introduction 数据增强与过拟合 验证是否过拟合的方法&#xff1a;画出loss曲线&#xff0c;如果训练集loss持续减小但是验证集loss增大&#xff0c;就说明是过拟合了。 数据增强目的 通过数据增强…