C++开散列(哈希桶)的模拟实现

C++开散列(哈希桶)的模拟实现

1.引言

哈希,就是一种映射关系,当我们把相同的值映射到相同容器的下标上时,这种容器就叫哈希表。不同的哈希表的不同映射规则就又组成了不同的哈希表。上述这种直接映射这种也是最简单的映射规则。

但若出现范围差距过大,如1,2,1000,2的情况,不可能开一个大小为1000的容器,所以想到了除留余数法。下标放数据大小模上容器大小的余数即可。

但这样不同值如12,2等又可能映射到同一个位置上,这种状况又称哈希冲突

为了解决哈希冲突,我们才引入哈希冲突的解决方案之一:哈希桶

C++哈希开散列,又称哈希桶,是为了解决哈希冲突而诞生的方法。它的核心目的不是避免冲突,哈希冲突或多或少都是不可避免的,而是尽量减少冲突。

而哈希桶的特色是什么呢?在哈希桶中,会冲突的值采用单链表节点的方式链接,只把头指针放入哈希表中即可。

在这里插入图片描述

2.实现说明

在本次模拟实现哈希桶中,我们会具体实现:哈希节点HashNode哈希桶类HashBucket、以及里面的**插入Insert()**函数、**删除Erase()**函数、**查找Find()**函数、以及测试函数等。

2.1哈希节点HashNode

​ 哈希节点本事要用pair对存放kv数据,然后又需要实现单链表的结构,所以又需要一个_next指针,只需这二者即可。

template<class K,class V>
	struct HashNode
	{
		HashNode<K, V>* _next;  //_next下一个指针
		pair<K, V> _kv; //存放kv数据的pair对

		HashNode(const pair<K,V>& kv) //初始化构造函数
			:_next(nullptr)
			,_kv(kv)
		{}
	};

2.2哈希桶类HashBucket

哈希桶类的基本结构如下:

template<class K,class V>
	class HashBucket
	{
		typedef HashNode<K, V> Node;
	public:
		Node* Find(const K& key)  //Find查找函数
		{}


		bool Erase(const K& key) //Erase删除函数
		{}

		bool Insert(const pair<K, V>& kv) //Insert插入函数
		{}



	private:
		vector<Node*> _tables; // 指针数组
		size_t _n = 0; //有效数据个数
	};

2.3Insert()插入函数

插入数据之前一般要用**负载因子(有效数据个数/容器大小)**判断是否要扩容,一般的如闭散列的负载因子控制在0.7-0.8左右,但是这个哈希桶由于其链式结构特殊,只需等到数据放满(负载因子 == 1)再扩容即可。

需要扩容的话,需要新开个新表代替,而且映射关系要重新建立

若不需要扩容,则采用头插即可。

bool Insert(const pair<K, V>& kv)
		{
			if (Find(kv.first))
			{
				return false;
			}
			//负载因子等于1才扩容 哈希桶的话

			if (_n == _tables.size())  //扩容的话需要新开一个vector数组,还需要重新映射对应关系
			{
				size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;

				vector<Node*> newtables(newsize,nullptr);
				for (Node*& cur : _tables)
				{
					while (cur)
					{
						Node* next = cur->_next;
						size_t hashi = cur->_kv.first % newtables.size();

						//头插到新表
						cur->_next = newtables[hashi];
						newtables[hashi] = cur;
						cur = next;
					}
				}
				_tables.swap(newtables);

			}

			size_t hashi = kv.first % _tables.size();  //要插入数据对应的hash下标
			//头插
			Node* newnode = new Node(kv);
			newnode->_next = _tables[hashi];
			_tables[hashi] = newnode;
			++_n;

			return true;
		}

2.4Erase()删除函数

删除节点的话需要分为两种情况:

1.删除的是头节点,那么删除前就需要保存下来前一个,否则会丢失。

2.删除的是头节点以下的节点,那么按正常逻辑删除即可。

bool Erase(const K& key)
		{
			size_t hashi = key % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (cur->_kv.first == 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;
		}

2.5Find()查找函数

定义一个当前节点cur往下正常走即可,走之前需要判断size是否为0

Node* Find(const K& key)
		{
			if (_tables.size() == 0)
				return nullptr;

			size_t hashi = key % _tables.size();
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (cur->_kv.first == key)  //找到了正常返回
				{
					return cur;
				}
				cur = cur->_next;   //没找到往下走
			}
			return nullptr;  //走完了没找到就是没有
		}

2.6测试

采用以下代码测试

void test_HashBucket()
	{
		int a[] = {3,33,2,13,5,12,1002};
		HashBucket<int, int> ht;
		for (auto e : a)
		{
			ht.Insert(make_pair(e,e));
		}
		cout<<ht.Find(13);

	}

在这里插入图片描述

结果测试成功!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Arthur___Cui

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值