【C++】哈希对unordered_map和unodered_set的封装

在这里插入图片描述

🚀write in front🚀
📜所属专栏: C++学习
🛰️博客主页:睿睿的博客主页
🛰️代码仓库:🎉VS2022_C语言仓库
🎡您的点赞、关注、收藏、评论,是对我最大的激励和支持!!!
关注我,关注我,关注我你们将会看到更多的优质内容!!

在这里插入图片描述

前言

  在前面的学习里面,我们完成了红黑树对map和set的封装,今天我们就用哈希里面的开散列对unordered_map和unodered_set进行封装。其实哈希的封装和红黑树的封装是非常相像的,所以我们这里简单道来。

一.哈希表的修改

  和红黑树一样,我们把模板参数改成:

template<class K,class T,class KeyOfT,class HashFunc = DefaultHashFunc<K>>
class HashTable
{}

K:关键码类型
T:对于unordered_map,T就是有个键值对;对于unordered_set,T就是Key。
keyOfT:取出元素(主要是为unordered_map设计)
HashFunc:仿函数,将key转换成整数,才能进行取模。
结点改为:

template<class T>
	struct HashNode
	{
		T _data;
		HashNode<T>* _next=nullptr;

		HashNode(const T& data):_data(data)
		{}
	};

二.封装map和set

对于map和set,我们也是直接修改一下模板传过来的值就可以了。

三.普通迭代器

template<class K, class T,class Ptr,class Ref, class KeyOfT, class  HashFunc>
	struct HTIterator
	{
	public:

		typedef HashNode<T> Node;
		typedef HTIterator<K, T,Ptr,Ref, KeyOfT, HashFunc> Self;
		typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> iterator;

		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& t)
		{
			return _node != t._node;
		}
	};

在这里对于迭代器的++操作,要寻找下一个元素就有两种情况,链表的下一个或者下一个哈希桶,所以这里为了找到下一个哈希桶,就要把哈希传过来,所以这里要传哈希的指针,为了使迭代器可以写出哈希的指针,这里的迭代器的模板参数也要有keyofThashfunc

	Self& operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();
				hashi++;
				while (hashi < _pht->_table.size())
				{
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						return *this;
					}
					hashi++;
				}

					_node = nullptr;
			}

			return *this;
		}

四.const迭代器

  随后我们就可以构造出const迭代器了。随后我们就要写出用普通迭代器构造const迭代器的构造函数。并且这里要在hashtable之前使用hashtable,就要先申明一下:

//这里就别写= DefaultHashFunc<K> 了,不然会保错:重定义参数
	template<class K, class T, class KeyOfT, class  HashFunc>
	class HashTable;
	template<class K, class T,class Ptr,class Ref, class KeyOfT, class  HashFunc>
	struct HTIterator
	{
	public:

		typedef HashNode<T> Node;
		typedef HTIterator<K, T,Ptr,Ref, KeyOfT, HashFunc> Self;
		typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> iterator;

		Node* _node;

		//这里如果传过来的是const类型的pht,权限会放大,所以说要改成下面的情况:
		//HashTable <K, T, KeyOfT, HashFunc>* _pht;
		/*HTIterator(Node* node, HashTable <K,T,KeyOfT, HashFunc>* pht) :_node(node),_pht(pht)
		{}*/

		//因为在这里我们对于这个哈希桶不会进行修改,所以直接用const修饰,这样只会出现权限的缩小或平移
		const HashTable <K, T, KeyOfT, HashFunc>* _pht;
		HTIterator(Node* node, const HashTable <K, T, KeyOfT, HashFunc>* pht) :_node(node), _pht(pht)
		{}

		HTIterator(const iterator& it) :_node(it._node), _pht(it._pht)
		{}

		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			
			return &(_node->_data);
		}

		Self& operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();
				hashi++;
				while (hashi < _pht->_table.size())
				{
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						return *this;
					}
					hashi++;
				}

					_node = nullptr;
			}

			return *this;
		}

		bool operator!=(const Self& t)
		{
			return _node != t._node;
		}
	};

五.insert返回值,operator[]和key不能修改的问题

  这里和红黑树封装map和set类似,就不用多说了。下面看看完整代码:

#pragma once
#include<vector>
#include<iostream>
using namespace std;


template<class T>
class DefaultHashFunc
{
public:
	size_t operator()(const T& key)
	{
		return (size_t)key;
	}
};

template<>
class DefaultHashFunc<string>
{
public:
	size_t operator()(const string& key)
	{
		size_t value = 1;
		for (auto& e : key)
		{
			value *= 131;
			value += e;
		}
		return value;
	}
};
namespace hash_bucket
{
	template<class T>
	struct HashNode
	{
		T _data;
		HashNode<T>* _next=nullptr;

		HashNode(const T& data):_data(data)
		{}
	};


	//这里就别写== DefaultHashFunc<K> 了,不然会保错:重定义参数
	template<class K, class T, class KeyOfT, class  HashFunc >
	class HashTable;

	template<class K, class T,class Ptr,class Ref, class KeyOfT, class  HashFunc>
	struct HTIterator
	{
	public:

		typedef HashNode<T> Node;
		typedef HTIterator<K, T,Ptr,Ref, KeyOfT, HashFunc> Self;
		typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> iterator;

		Node* _node;

		//这里如果传过来的是const类型的pht,权限会放大,所以说要改成下面的情况:
		//HashTable <K, T, KeyOfT, HashFunc>* _pht;
		/*HTIterator(Node* node, HashTable <K,T,KeyOfT, HashFunc>* pht) :_node(node),_pht(pht)
		{}*/

		//因为在这里我们对于这个哈希桶不会进行修改,所以直接用const修饰,这样只会出现权限的缩小或平移
		const HashTable <K, T, KeyOfT, HashFunc>* _pht;
		HTIterator(Node* node, const HashTable <K, T, KeyOfT, HashFunc>* pht) :_node(node), _pht(pht)
		{}

		HTIterator(const iterator& it) :_node(it._node), _pht(it._pht)
		{}

		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &(_node->_data);
		}

		Self& operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();
				hashi++;
				while (hashi < _pht->_table.size())
				{
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						return *this;
					}
					hashi++;
				}

					_node = nullptr;
			}

			return *this;
		}

		bool operator!=(const Self& t)
		{
			return _node != t._node;
		}
	};

	template<class K,class T, class KeyOfT,class HashFunc= DefaultHashFunc<K> >
	class HashTable
	{
		typedef HashNode<T> Node;
		//友员声明
		template<class K, class T, class Ptr,class Ref,class KeyOfT, class HashFunc>
		friend struct HTIterator;
	public:
		HashTable()
		{
			_table.resize(10,nullptr);
		}

		~HashTable()
		{
			for (int i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				while (cur)
				{
					Node* next = cur->_next;
					delete cur;
					cur = next;
					
				}

				_table[i] = nullptr;
			}
		}

		typedef HTIterator<K, T,T*,T&, KeyOfT, HashFunc>  iterator;
		typedef HTIterator<K, T, const T*, const T&, KeyOfT, HashFunc>  const_iterator;


		iterator begin()
		{
			for (int i = 0; i < _table.size(); i++)
			{
				if (_table[i])
				{
					return iterator(_table[i], this);
				}
			}

			return iterator(nullptr, this);
		}


		iterator end()
		{
			return iterator(nullptr, this);
		}

		const_iterator begin() const
		{
			for (int i = 0; i < _table.size(); i++)
			{
				if (_table[i])
				{
					return const_iterator(_table[i], this);
				}
			}

			return const_iterator(nullptr, this);
		}


		const_iterator end() const
		{
			return const_iterator(nullptr, this);
		}

		pair<iterator,bool> Insert(const T& data)
		{
			KeyOfT kot;
			auto it = Find(kot(data));
			if (it!=end())
			{
				return make_pair(it,true);
			}
			HashFunc hf;
			if (_table.size() == _n)
			{
				//扩容

				HashTable newhash;
				size_t newsize = _table.size() * 2;
				newhash._table.resize(newsize);
				for (int i = 0; i < _table.size(); i++)
				{
					Node* cur = _table[i];
					while (cur)
					{
						Node* next = cur->_next;
						
						size_t hashi = hf(kot(cur->_data)) % newsize;
						cur->_next = newhash._table[i];
						newhash._table[i] = cur;

						cur = next;
					}
					_table[i] = nullptr;
				}

				_table.swap(newhash._table);
			}
			
			size_t hashi = hf(kot(data)) % _table.size();
			Node* cur = new Node(data);
			cur->_next = _table[hashi];
			_table[hashi] = cur;
			_n++;

			return make_pair(iterator(cur,this),false);
			
		}



		iterator Find(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* cur = _table[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(cur,this);
				}
				cur = cur->_next;
			}

			return end();
		}

		bool Erase(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* cur = _table[hashi];
			Node* prev = nullptr;
			while (cur)
			{
				
				if (kot(cur->_data) ==key)
				{
					if (prev == nullptr)
					{
						_table[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}
					delete cur;
					cur = nullptr;
					return true;
				}

				prev = cur;
				cur = cur->_next;
			}

			return false;

			
		}

		void Print()
		{
			for (int i = 0; i < _table.size(); i++)
			{
				printf("[%d]: ", i);
				Node* cur = _table[i];

				while (cur)
				{
					cout << "("<<cur->_kv.first << ":" << cur->_kv.second<<")" << " ->";
					cur = cur->_next;
				}
				printf("NULL\n");
			}
		}
	private:
		vector<Node*>  _table;
		size_t _n=0;
	};
}

set的封装:

namespace zxr
{
	template<class K>
	class unordered_set
	{
	private:
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
		hash_bucket::HashTable<K, K,SetKeyOfT> _ht;

	public:

		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;

		iterator begin()const
		{
			return _ht.begin();
		}
		iterator end()const
		{
			return _ht.end();
		}
		pair<iterator,bool> insert(const K&data)
		{
			pair<typename hash_bucket::HashTable<K, K, SetKeyOfT>::iterator, bool> it= _ht.Insert(data);
			return pair<iterator,bool>(it.first, it.second);
			
		}

		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}
		bool find(const K& key)
		{
			return _ht.Find(key);
		}
	};
}

map的封装:

namespace zxr
{
	template<class K,class V>
	class unordered_map
	{
	private:
		struct MapKeyOfT
		{
			const K& operator()(const pair<const K,V>& kv)
			{
				return kv.first;
			}
		};
		hash_bucket::HashTable<K, pair<const K,V>, MapKeyOfT> _ht;

	public:
		typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;
		iterator begin()
		{
			return _ht.begin();
		}
		iterator end()
		{
			return _ht.end();
		}

		const_iterator begin()const
		{
			return _ht.begin();
		}
		const_iterator end()const
		{
			return _ht.end();
		}

		pair<iterator,bool> insert(const pair<const K,V>& data)
		{
			return _ht.Insert(data);
		}

		V& operator [](const K& key)
		{
			auto it = insert(make_pair(key,V()));
			return it.first->second;
		}
		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}
		bool find(const K& key)
		{
			return _ht.Find(key);
		}
	};
}

总结

在这里插入图片描述
  在这里我们在讲几个之前没理解的点,我们的迭代器在平时可以用const迭代器来介绍迭代器,就是因为库里面已经写了用普通迭代器构造const迭代器的构造函数,随意我们才能理所当然的使用。
这里要强调的还是,同一模板传了不同参数,此时就生成了两个不同的类型,无论模板参数是const还是普通,都不在是同一个类型了。
在这里插入图片描述

封装的过程都是这样的:
1、哈希表
2、封装map和set
3、普通迭代器
4、const迭代器
5、insert返回值 operator[]
6、key不能修改的问题

  更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

专栏订阅:
每日一题
C语言学习
算法
智力题
初阶数据结构
Linux学习
C++学习
更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小参宿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值