【C++进阶】哈希表封装实现unordered_set和unordered_map

改造哈希表

我们先来看一段代码。
在这里插入图片描述
我们知道unordered_set和unordered_map的底层是用哈希表实现的,而unordered_set的数据key值,unordered_map的数据是key和value的键值对。从上图的代码中可以看出,哈希表的节点可以支持存unordered_map的数据,但是unordered_set的数据呢?难道我们要再实现一个哈希表,它的节点里面存的是key吗?其实也不是不可以,但是这样大部分代码就冗余了不好。那么我们该怎样改造哈希表呢?

模板参数的改造

在这里插入图片描述
在这里插入图片描述
我们可以进行以上代码的改造,我们的底层哈希表确实不知道,要存的是key的数据,还是key和value的键值对数据,所以我们直接让哈希表节点的模板参数<class K,class V>变成<class T>,哈希表的模板参数也随之改变这样底层的哈希表什么类型都可以接受,而这时就需要我们上层的unordered_set和unordered_map显示的传数据类型过去
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
大家可能也看到了哈希表模板参数的后两个参数KeyOfT和HashFunc这里我们先不介绍,下面用到了我们在介绍。

插入的改造

我们先来看看模板参数改造后,原来的插入代码会暴露出哪些问题?
在这里插入图片描述
首先第一个问题就是参数问题
在这里插入图片描述
模板参数改造后存的数据类型已经不是键值对了,而是T类型。
在这里插入图片描述
接下来就迎来了第二个问题,插入位置计算问题。
在这里插入图片描述
我们知道插入数据时,要计算数据在哈希表中对应的坐标。那么改造后的T类型数据我们能不能直接进行计算呢?传过来的要是key类型我们可以进行计算,但是传过来的要是键值对呢?那么我们这时就需要增加一层仿函数。我们的底层哈希表确实不知道传的是什么类型的数据,能不能用除留余数法进行计算。但是我们的封装层unordered_set和unordered_map知道,我们显示的传仿函数过去就可以,不同类型的数据,就调用不同类型的仿函数。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里就是哈希表第三模板参数KeyOfT的作用。
在这里插入图片描述

在这里插入图片描述
蓝色框里面的hf是另一个问题,我们接下来再解答。
到这里我们又遇到一个问题,这里我们数据类型如果是整形我们可以用除留余数法计算坐标,但是如果是浮点型和字符串类型呢?我们该如何计算?
这里我们就要用到仿函数,用仿函数把数据间接或者直接转化成整形
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

删除和查找的改造

删除和查找并没有什么其它特别的改造,主要面临的问题和插入几乎一致所以就不过多讲述。主要放代码让大家对比他们之间的区别。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
到这哈希表本身的改造就完成了,我们还有一个重要的任务就是增加哈希表的迭代器。

增加迭代器

迭代器的结构

在这里插入图片描述

这里我们可以知道,哈希表的迭代器里面有两个成员变量,一个是节点指针,一个是哈希表指针。那么这里就有一个问题了,为什么要多存一个哈希表指针呢?有什么用呢?其实这个哈希表的指针用处巨大,因为我们要用迭代器最重要的是要对迭代器进行++或者--没有哈希表我们要对哈希表的节点进行++或者–该如何进行呢?我们如何迭代的往下走?几乎没有办法,我们没有哈希表指针寸步难行。所以这里我们多加了一个哈希表指针。

构造函数

在这里插入图片描述

operator*和operator->的实现

在这里插入图片描述

operator!=和operator==的实现

在这里插入图片描述

前置operator++的实现

哈希表节点++的思想:
首先先看一下被++节点的后面还有没有数据,如果有那么下一个访问的节点就是它。
如果没有就继续往后找不为空的桶,如果找到了那么下一个访问的就是这个不为空桶的第一个节点。如果没有找到,那么就说明所有节点都访问完毕了。
举个例子:
在这里插入图片描述

我们假设it是迭代器指针的它指向11,it++后该指向的节点是谁呢?是21。
在这里插入图片描述
如果it指向31呢?
在这里插入图片描述
it++后该指向的节点是谁呢?是3。
在这里插入图片描述
就是应用这样的思路进行++。
代码实现如下:
在这里插入图片描述

后置operator++的实现

后置++和前置++思路一样。这里就不过多介绍。只不过是返回值的问题。大家有兴趣可以自己去了解一下
在这里插入图片描述
–的思路和++差不多,只不过–是从尾部开始往前走,++是从头部开始往后走。大家可以自己去尝试实现一下。

完整版代码

HashTable.h

#pragma once
#include <vector>
template<class K>
struct Hash
{
	size_t operator()(const K& key)
	{
		return key;
	}
};
template<>
struct Hash<string>
{
	size_t operator()(const string& s)
	{
		size_t value = 0;
		for (auto& ch : s)
		{
			value += ch;
		}
		return value;
	}
};

namespace LinkHash
{
	template<class T>
	struct HashNode
	{
		T _data;
		HashNode<T>* next;

		HashNode(const T& data)
			:_data(data)
			,next(nullptr)
		{}

	};
	template<class K, class T, class KeyOfT, class HashFunc>
	class HashTable;

	template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc>
	struct HTIterator
	{
		typedef HashNode<T> Node;
		typedef HTIterator<K, T, Ref, Ptr, KeyOfT, HashFunc> Self;
		
		Node* _node;
		HashTable<K, T, KeyOfT, HashFunc>* _pht;

		HTIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			,_pht(pht)
		{}

		Ref operator*()
		{
			return _node->_data;
		}
		Ptr operator->()
		{
			return &_node->_data;
		}
		bool operator==(const Self& it)
		{
			return _node == it._node;
		}
		bool operator!=(const Self& it)
		{
			return _node != it._node;
		}

		Self& operator++()
		{
			Node* cur = _node->next;
			
			if (cur)
			{
				_node = cur;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t index = hf(kot(_node->_data)) % _pht->_tables.size();
				index++;

				while (index < _pht->_tables.size() && !_pht->_tables[index])
				{
					index++;
				}

				if (index < _pht->_tables.size())
					_node = _pht->_tables[index];
				else
					_node = nullptr;
			}

			return *this;
		}
		
		Self operator++(int)
		{
			Node* record = _node;
			Node* cur = _node->next;

			if (cur)
			{
				_node = cur;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t index = hf(kot(_node->_data)) % _pht->_tables.size();
				index++;

				while (index < _pht->_tables.size() && !_pht->_tables[index])
				{
					index++;
				}

				if (index < _pht->_tables.size())
					_node = _pht->_tables[index];
				else
					_node = nullptr;
			}

			return HTIterator(record,_pht);
		}
	
	};

	template<class K, class T, class KeyOfT, class HashFunc>
	class HashTable
	{
		typedef HashNode<T> Node;
		template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc>
		friend struct HTIterator;
		typedef HashTable<K, T, KeyOfT, HashFunc> Self;
	public:
		typedef HTIterator<K, T, T&, T*, KeyOfT, HashFunc> iterator;
		HashTable() = default;

		HashTable(const Self& ht)
		{
			_tables.resize(ht._tables.size());
			for (size_t i = 0; i < ht._tables.size(); i++)
			{
				Node* cur = ht._tables[i];
				while (cur)
				{
					Node* newnode = new Node(cur->_data);
					
					Node* next = _tables[i];
					_tables[i] = newnode;
					newnode->next = next;

					cur = cur->next;
				}
			}
		}
		Self& operator=(Self ht)
		{
			swap(_size, ht._size);
			_tables.swap(ht._tables);

			return *this;
		}
		~HashTable()
		{
			for (size_t i = 0; i < _tables.size(); i++)
			{
				Node* cur = _tables[i];
				while (cur)
				{
					Node* next = cur->next;
					delete cur;
					cur = next;
				}
				_tables[i] = nullptr;
			}
		}
		iterator begin()
		{
			size_t index = 0;
			while (index < _tables.size() && !_tables[index])
			{
				index++;
			}
			return iterator(_tables[index], this);
		}
		iterator end()
		{
			return iterator(nullptr, this);
		}
		bool Erase(const K& key)
		{
			if (_size == 0)
			{
				return false;
			}
			HashFunc hf;
			KeyOfT kot;
			size_t index = hf(key) % _tables.size();
			Node* cur = _tables[index];
			Node* prev = nullptr;
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					Node* next = cur->next;
					
					if(prev)
						prev->next = next;
					else
						_tables[index] = next;

					delete cur;
					cur = nullptr;
					_size--;
					return true;
				}
				else
				{
					prev = cur;
					cur = cur->next;
				}
			}
			return false;
		}
		Node* Find(const K& key)
		{
			if (_size == 0)
			{
				return nullptr;
			}
			HashFunc hf;
			KeyOfT kot;
			size_t index = hf(key) % _tables.size();
			Node* cur = _tables[index];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return cur;
				}
				else
				{
					cur = cur->next;
				}
			}
			return nullptr;
		}
		bool Insert(const T& data)
		{
			KeyOfT kot;
			Node* ret = Find(kot(data));
			if (ret)
			{
				return false;
			}
			if (_tables.size() == 0 || _size*10 / _tables.size() == 10)
			{
				size_t newsize = _tables.size() == 0 ? 10 : 2 * _tables.size();
				HashTable<K,T,KeyOfT,HashFunc> newHT;
				newHT._tables.resize(newsize);
				HashFunc hf;
				for (int i = 0; i < _tables.size(); i++)
				{
						Node* cur = _tables[i];
						while (cur)
						{
							Node* next = cur->next;
							size_t index = hf(kot(cur->_data)) % newHT._tables.size();
							if (newHT._tables[index])
							{
								Node* next = newHT._tables[index];
								newHT._tables[index] = cur;
								cur->next = next;
							}
							else
							{
								newHT._tables[index] = cur;
								newHT._tables[index]->next = nullptr;;
							}
							newHT._size++;
							cur = next;
						}
						_tables[i] = nullptr;	
				}
				_tables.swap(newHT._tables);
			}
			HashFunc hf;
			Node* cur = new Node(data);
			size_t index = hf(kot(data)) % _tables.size();
			if (_tables[index])
			{
				Node* next = _tables[index];
				_tables[index] = cur;
				cur->next = next;
			}
			else
			{
				_tables[index] = cur;
			}
			_size++;
			return true;
		}
	private:
		vector<Node*> _tables;
		size_t _size = 0;
	};
	
}

unordered_set.h

#pragma once
#include"HashTable.h"
namespace lzf
{
	template<class K>
	struct SetKeyOfT
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
	template<class K>
	class unorderset
	{
	public:
		typedef typename LinkHash::HashTable<K, K, SetKeyOfT<K>, Hash<K>>::iterator iterator;
		iterator begin()
		{
			return _ht.begin();
		}
		iterator end()
		{
			return _ht.end();
		}

		bool insert(const K& key)
		{
			return _ht.Insert(key);
		}
		
	private:
		LinkHash::HashTable<K, K, SetKeyOfT<K>,Hash<K>> _ht;
	};
	void test_unorderset()
	{
		unorderset<string> us;
		us.insert("left");
		us.insert("right");
		us.insert("map");
		us.insert("further");
		us.insert("rate");
		us.insert("grain");

		unorderset<string>::iterator it = us.begin();
		while (it != us.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

		unorderset<string> uss = us;
		unorderset<string>::iterator it1 = uss.begin();
		while (it1 != uss.end())
		{
			cout << *it1 << " ";
			++it1;
		}
	}
}

unordered_map.h

#pragma once
#include"HashTable.h"
#include<string>
namespace lzf
{
	template<class K,class V>
	struct MapKeyOfT
	{
		const K& operator()(const pair<K,V>& kv)
		{
			return kv.first;
		}
	};
	template<class K,class V>
	class unordermap
	{
	public:
		typedef typename LinkHash::HashTable<K, pair<K,V>, MapKeyOfT<K,V>, Hash<K>>::iterator iterator;
		iterator begin()
		{
			return _ht.begin();
		}
		iterator end()
		{
			return _ht.end();
		}

		bool insert(const pair<K,V>& kv)
		{
			return _ht.Insert(kv);
		}
	private:
		LinkHash::HashTable<K, pair<K,V>, MapKeyOfT<K,V>, Hash<K>> _ht;
	};

	void testunordermap()
	{
		unordermap<string, string> um;
		um.insert(make_pair("left", ""));
		um.insert(make_pair("right", "ұ"));
		um.insert(make_pair("map", "ͼ"));
		um.insert(make_pair("set", ""));

		unordermap<string, string>::iterator it = um.begin();
		while (it != um.end())
		{
			cout << it->first << ":" << it->second << endl;
			++it;
		}
		
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值