哈希表+unordered_map封装

news2024/11/26 15:51:21

目录

 1:hashtable.h

2:unordered_map.h

 3:unordered_set

4:讲解


 1:hashtable.h

#pragma once
#include<vector>
using namespace std;
namespace OpenAdress
{
	enum State
	{
		EXIST,
		DELETE,
		EMPTY,
	};
	template<class K,class V>
	struct HashData
	{
		pair<K, V> _kv;
		State _state = EMPTY;
	};



	template<class K, class V>
	class HashTable
	{
	public:
		bool Insert(const pair<K, V>& kv)
		{
			if (Find(kv.first))
			{
				return false;
			}

			if (_tables.size() == 0 || (_n * 10 / _tables.size()) >= 7)
			{
				size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				HashTable<K, V> _newht;
				_newht._tables.resize(newsize);//必须开新表,不能直接写,否则映射关系改变
				for (auto& data : _tables)
				{
					_newht.Insert(data._kv);
				}
				_tables.swap(_newht._tables);
			}

			size_t hashi = kv.first % _tables.size();
			size_t i = 1;
			size_t index = hashi;
			while (_tables[index]._state == EXIST)//3 13 23 33删除23 如果判断
			{
				index = hashi + i;
				index %= _tables.size();
				++i;
			}
			_tables[index]._kv = kv;
			_tables[index]._state = EXIST;
			++_n;
			return true;
		}
		HashData<K, V>* Find(const K& key)
		{
			if (_tables.size() == 0)
			{
				return nullptr;
			}
			size_t hashi =key % _tables.size();
			//等于删除也往后找,防止有时候查找数据还删数据,如果判断为非存在就有问题了,过不去后面的数据
			size_t i = 1;
			size_t index = hashi;
			while (_tables[index]._state != EMPTY)
			{
				if (_tables[index]._kv.first == key
					&& _tables[index]._state == EXIST)//如果已经删了再找就找不到了
				{
					return &_tables[index];
				}
				index = i + hashi;
				index %= _tables.size();
				++i;
				if (index == hashi)//找了一圈,全是存在和删除
				{
					break;
				}
			}
			//有两可能,一个是index==hashi,找了一圈全是存在和删除,还有一种可能是一开始index=hashi的时候后面就没有数据了。
			return nullptr;
		}
		bool Erase(const K& key)
		{
			HashData<K, V>* k = Find(key);
			if (k)
			{
				k->_state = DELETE;
				--_n;
					return true;
			}
			else
			{
				return false;
			}
		}
	private:
		vector<HashData<K, V>> _tables;
		size_t _n = 0;
	};
}
namespace HashBucket
{
	template<class T>
	struct HashNode
	{
		HashNode<T>* _next;
		T _data;
		HashNode(const T& data)
			:_data(data)
			,_next(nullptr){}
	};
	template<class K, class V, class keyoft>
	class HashTable;
	template<class K, class T, class Ptr, class Ref, class keyoft>
	class _HashIterator
	{
	public:
		typedef HashNode<T> Node;
		typedef HashTable<K, T, keyoft> HT;
		typedef _HashIterator<K, T, Ptr, Ref, keyoft> Self;
		typedef _HashIterator<K, T, T*, T&, keyoft> iterator;

		Node* _node;
		HT* _ht;
		_HashIterator(Node* node,HT* ht)
			:_node(node)
			,_ht(ht){}

		_HashIterator(const iterator& it)
			:_node(it._node)
			,_ht(it._ht){}

		Ref operator*()
		{
			return _node->_data;
		}
		Ptr operator->()
		{
			return &_node->_data;
		}
		bool operator!=(const Self& s)
		{
			return _node != s._node;
		}
		Self& operator++()
		{
			if (_node->_next != nullptr)
			{
				_node = _node->_next;
			}
			else
			{
				keyoft kot;
				size_t hashi = kot(_node->_data) % _ht->_tables.size();
				++hashi;
				while (hashi < _ht->_tables.size())
				{
					if (_ht->_tables[hashi])
					{
						_node = _ht->_tables[hashi];
						break;
					}
					else
					{
						++hashi;
					}
				}
				if (hashi == _ht->_tables.size())
				{
					_node = nullptr;
				}
				return *this;
			}
		}
			
	};
	template<class K,class T,class keyoft>
	class HashTable
	{
	public:
		typedef HashNode<T> Node;
		typedef _HashIterator<K, T, T*, T&, keyoft> _iterator;
		typedef _HashIterator<K, T, const T*,const T&, keyoft> const_iterator;
		template<class K, class T, class Ptr, class Ref, class keyoft>
		friend class _HashIterator;

		_iterator begin()
		{
			Node* cur = nullptr;
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				cur = _tables[i];
				if (cur)
				{
					break;
				}

			}
			return _iterator(cur, this);
		}
		const_iterator begin()const
		{
			Node* cur = nullptr;
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				cur = _tables[i];
				if (cur)
				{
					break;
				}

			}
			return _iterator(cur, this);
		}
		_iterator end()
		{
			return _iterator(nullptr, this);
		}
		const_iterator end()const
		{
			return _iterator(nullptr, this);
		}
		_iterator find(const K& key)
		{
			if (_tables.size() == 0)
			{
				return end();
			}
			keyoft kot;
			size_t hashi = key % _tables.size();
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return _iterator(cur, this);
				}
				cur = cur->_next;
			}
			return end();
		}
		~HashTable()
		{
			for (auto& e : _tables)
			{
				while (e)
				{
					Node* next = e->_next;
					delete e;
					e = next;
				}
				e = nullptr;

			}
		}
		bool Insert(const T& data)
		{
			keyoft kot;

			if (_n == _tables.size())
			{
				//扩容
				size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				vector<Node*> newtables(newsize, nullptr);
				for (auto& cur : _tables)
				{
					while (cur)
					{
						Node* next = cur->_next;
						size_t hashi = kot(cur->_data) % newtables.size();
						cur->_next = newtables[hashi];
						newtables[hashi] = cur;
						cur = next;

					}
				}
				_tables.swap(newtables);
			}
			size_t hashi = kot(data) % _tables.size();
			Node* newnode = new Node(data);
			newnode->_next = _tables[hashi];
			_tables[hashi] = newnode;
			++_n;
			return true;
		}
		bool Erase(const K& key)
		{
			keyoft kot;
			size_t hashi = key % _tables.size();
			Node* _prev = nullptr;
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (_prev == nullptr)
					{
						_tables[hashi] = cur->_next;
					}
					else
					{
						_prev->_next = cur->_next;
					}
					delete cur;
					return true;
				}
				else
				{
					_prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}
	private:

		vector<Node*> _tables;
		size_t _n = 0;
	};
}

2:unordered_map.h

template<class K,class V>
class unordered__map
{
	struct mapoft
	{
		const K& operator()(const pair<K, V>& kv)
		{
			return kv.first;
		}
	};
public:
	typedef typename HashBucket::HashTable<K, pair<const K, V>, mapoft>::_iterator iterator;
	typedef typename HashBucket::HashTable<K, pair<const K, V>, mapoft>::const_iterator const_iterator;

	bool Insert(const pair<K, V>& kv)
	{
		return _ht.Insert(kv);
	}
	bool Erase(const K& key)
	{
		return _ht.Erase(key);
	}
	iterator begin()
	{
		return _ht.begin();
	}
	iterator end()
	{
		return _ht.end();
	}
	
private:
	HashBucket::HashTable<K, pair<const K, V>, mapoft> _ht;
};

 3:unordered_set

#include"HashTable.h"
template<class K, class V>
class unordered__set
{
	struct setoft
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
public:
	typedef typename HashBucket::HashTable<K, pair<const K, V>, mapoft>::const_iterator iterator;
	typedef typename HashBucket::HashTable<K, pair<const K, V>, mapoft>::const_iterator const_iterator;

	bool Insert(const pair<K, V>& kv)
	{
		return _ht.Insert(kv);
	}
	bool Erase(const K& key)
	{
		return _ht.Erase(key);
	}
	iterator begin()
	{
		return _ht.begin();
	}
	iterator end()
	{
		return _ht.end();
	}

private:
	HashBucket::HashTable<K, pair<const K, V>, setoft> _ht;
};

4:讲解

K是用来find的时候使用的返回值,对于map来说不能没有。set可以没有,因为set是K K

V是上层传入的数据类型。

因为闭散列的哈希桶,如果迭代器++,有可能是在当前桶中遍历,也有可能是在vector中找下一个不为空的头节点,所以在迭代器中必须定义一个node和一个哈希桶指针ht。因为node里面不会存储下一个桶的地址。

迭代器要封装一个普通和const版本,对于set来说可以不需要普通迭代器,对于map来说则必须要,因为map的second可能会++。

 这里面如果set创建对象,是一个普通对象,普通对象return的是普通迭代器,但是函数返回值是const迭代器,所以这里就有从普通迭代器->const迭代器的转换。

还记得单参的构造函数可以隐式类型转换吗? 

在迭代器里面我们定义了iterator是一个普通迭代器,因此我们想用普通迭代器转换为const迭代器,意思就是用普通迭代器去构造一个const迭代器。

对于开散列的插入,我们可以复用自己的insert,对于闭散列,不要浪费原来的节点,插入新节点就行了。

keyoft是一个仿函数,因为底层hashtable并不清楚V是什么类型,所以上层通过传入(pair和k)不同的类型,来返回到first或者k本身。

operator[]我这里没有实现,实现思路是让底层insert的返回值为pair<iterator,bool>,第一步是用map里面的pair<iterator,bool> ret接收insert的返回值,这样既能插入,又可以判断是否已经存在这个值,然后返回ret的first的second,比如我们排序的时候会用m[e]++,就是这个意思

实际上map这一层还会多一个参数hashfunc,比较一下map和unordered_map,要满足V能成为映射值,也就是可以取整,所以需要设计一个string类的转化为size_t的仿函数。

  实际上针对扩容问题有一个更好的方案,让每次扩容的容量为质数。可以减少冲突

			static const unsigned long __stl_prime_list[__stl_num_primes] =
			{
				53, 97, 193, 389, 769,
				1543, 3079, 6151, 12289, 24593,
				49157, 98317, 196613, 393241, 786433,
				1572869, 3145739, 6291469, 12582917, 25165843,
				50331653, 100663319, 201326611, 402653189, 805306457,
				1610612741, 3221225473, 4294967291
			};

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

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

相关文章

redis淘汰策略

8种淘汰策略 volatile-lru&#xff0c;针对设置了过期时间的key&#xff0c;使用lru算法进行淘汰。 allkeys-lru&#xff0c;针对所有key使用lru算法进行淘汰。 volatile-lfu&#xff0c;针对设置了过期时间的key&#xff0c;使用lfu算法进行淘汰。 allkeys-lfu&#xff0c;针…

『C++』特殊类设计

「前言」文章是关于C特殊类设计方面的 「归属专栏」C嘎嘎 「笔者」枫叶先生(fy) 「座右铭」前行路上修真我 「枫叶先生有点文青病」 「每篇一句」 一点浩然气&#xff0c;千里快哉风。 ——苏轼《水调歌头黄州快哉亭赠张偓佺》 目录 一、请设计一个类&#xff0c;不能被拷贝 …

IDEA开发实现Maven+Servlet+Mybatis实现CRUD管理系统-Mapper代理开发

Mapper代理开发概述 之前我们写的代码是基本使用方式&#xff0c;它也存在硬编码的问题&#xff0c;如下&#xff1a; 这里调用 selectList() 方法传递的参数是映射配置文件中的 namespace.id值。这样写也不便于后期的维护。如果使用 Mapper 代理方式&#xff08;如下图&…

stm32之hal库学习(3)---STM32 启动模式分析

启动模式 我们知道的复位方式有三种&#xff1a;上电复位&#xff0c;硬件复位和软件复位。当产生复位&#xff0c;并且离开复 位状态后&#xff0c;CM3 内核做的第一件事就是读取下列两个 32 位整数的值&#xff1a; &#xff08;1&#xff09;从地址 0x0000 0000 处取出堆栈…

Linux基本指令----下

Linux基本指令----下 date指令cal指令find指令which指令whereis指令grep指令du指令zip/unzip指令tar指令bc指令uname -r指令关机指令重要热键结语 date指令 语法&#xff1a; date 指定格式显示时间&#xff1a; date %Y:%m:%d&#xff0c;date 用法&#xff1a;date [OPTION]…

【FPGA零基础学习之旅#5】产生非等占空比信号

&#x1f389;欢迎来到FPGA专栏~产生非等占空比信号 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒&#x1f379; ✨博客主页&#xff1a;小夏与酒的博客 &#x1f388;该系列文章专栏&#xff1a;FPGA学习之旅 文章作者技术和水平有限&#xff0c;如果文中出现错误&#xff0c;希望大家能…

字节跳动 测试开发工程师 面试总结,小伙伴可以参考一下

目录 一面 二面 三面 个人总结&#xff1a; 一面 自我介绍 操作系统 虚拟内存的概念 进程和线程 线程同步 如何实现线程同步 计算机网络 说一下TCP 为什么是四次挥手呢 说一个ip地址&#xff0c;如果第一字节是269行不行 说一下HTTP&#xff08;自己讲了GET POST&…

[abc复盘] abc301 20230514

[abc复盘] abc301 20230514 总结A - Overall Winner1. 题目描述2. 思路分析3. 代码实现 B - Fill the Gaps1. 题目描述2. 思路分析3. 代码实现 C - AtCoder Cards1. 题目描述2. 思路分析3. 代码实现 D - Bitmask1. 题目描述2. 思路分析3. 代码实现 E - Pac-Takahashi1. 题目描…

Azure描述云服务类型

Azure描述云服务类型 基础结构即服务&#xff08;IaaS&#xff09;共担责任模型方案 介绍平台即服务&#xff08;PaaS&#xff09;共担责任模型方案 描述软件即服务&#xff08;SaaS&#xff09;共担责任模型方案 描述责任共担模型知识检查题目 基础结构即服务&#xff08;IaaS…

pyqt实现文件批量操作

代码逻辑 https://download.csdn.net/download/Lynqwest/87783077 文件打包 一、安装UPX 在https://github.com/upx/upx/releases/tag/v3.96下载相关版本&#xff0c;该网址无法进入&#xff0c;可参考https://download.csdn.net/download/Lynqwest/87783084 下载后将 upx…

谓词逻辑与推理演算

谓词逻辑 辖域 变元的约束—换自由变元 不容易出错 枚举 前束范式 量词例子 全称量词 ( ∀ x ) 条件前件加入 → (\forall x) 条件前件加入 \to (∀x)条件前件加入→ 存在量词 ( ∃ x ) 和取式 ∧ (\exists x) 和取式 \wedge (∃x)和取式∧ ∀ x P ( x ) ⟺ ∃ x P ( x ) \…

Mac M2芯片免安装版mysql

文章目录 1、下载mysql安装包2、移动解压目录并授权3、初始化mysql4、启动mysql5、启动错误处理6、登录mysql7、重置mysql密码 1、下载mysql安装包 先看一下本机mac信息 左上角&#xff0c;单机苹果的logo&#xff0c;然后单击“关于本机”&#xff0c;可以看到当前mac的信息 …

【JAVA进阶】Set集合、Map集合

&#x1f4c3;个人主页&#xff1a;个人主页 &#x1f525;系列专栏&#xff1a;JAVASE基础 目录 一、Set系列集合 1.HashSet 2.LinkedHashSet 3.TreeSet 二、补充知识 1.可变参数 2.集合工具类Collections 三、Map集合体系 1.Map集合的概述 2.Map集合体系特点 3.Map…

mybatis plus自动生成代码(代码生成器)

参考 05_尚硅谷_搭建项目环境&#xff08;代码生成器&#xff09;_哔哩哔哩_bilibili 调用mp的AutoGenerator可以生成代码&#xff0c;就像java脚本一样&#xff0c;运行即可生成 要求代码生成器脚本不用会写&#xff0c;会修改其中条项生成我们预期的代码即可。 项目整体结构…

前端 之 FormData对象浅谈

一、简介 ​ 通常情况下&#xff0c;前端在使用post请求提交数据的时候&#xff0c;请求都是采用application/json 或 application/x-www-form-urlencoded编码类型&#xff0c;分别是借助JSON字符串来传递参数或者keyvalue格式字符串&#xff08;多参数通过&进行连接&…

mediasoup Transport端口策略

一. 前言 mediasoup 支持多种类型的 Transport&#xff0c;有 WebRtcTransport&#xff0c;PlainTransport 以及 PipeTransport&#xff0c;对于 WebRtcTransport 目前 mediasoup 最新版本已经支持多个 WebRtcTransport 共用单个端口的模式了&#xff0c;而在此之前每个 WebRtc…

真题详解(补码转换)-软件设计(七十四)

真题详解(索引查询)-软件设计&#xff08;七十三)https://blog.csdn.net/ke1ying/article/details/130659024 Composite模式&#xff1a; 以树形结构来表示”整体-部分”的关系&#xff0c;使得单个和团体的使用都具有一致性。 对一个基本有序的数组进行排序&#xff0c;适合…

【PCIE体系结构九】物理层的基本逻辑框架

&#x1f449;个人主页&#xff1a;highman110 &#x1f449;作者简介&#xff1a;一名硬件工程师&#xff0c;持续学习&#xff0c;不断记录&#xff0c;保持思考&#xff0c;输出干货内容 参考书籍&#xff1a;《深入浅出SSD&#xff1a;固态存储核心技术、原理与实战》 物…

可见光遥感图像目标检测(三)文字场景检测之Arbitrary

前言 前面介绍了关于可见光遥感图像目标检测任务主要面临的问题&#xff0c;现在对旋转目标的问题进行优化&#xff0c;为了便于大家理解与之前通用目标检测区别&#xff0c;采用Faster-Rcnn网络模型的架构对旋转目标的检测进行改进。 本教程禁止转载。同时&#xff0c;本教程来…

5G-NR非连续接收DRX参数配置详解

5G-NR系统配置中的非连续接收系统 DRX&#xff1a;Discontinuous Reception 5G终端商用在即&#xff0c;根据前期测试及部分5G友好用户反馈&#xff0c;“5G终端功耗大&#xff0c;待机差”问题特别突出。根据5G技术特性&#xff0c;导致5G终端相比4G功耗大很多的原因有如下4…