unordered_map与unordered_set的封装

在STL中,unordered_map与unordered_set的查找是天花板级别的,他们的底层逻辑很简单,就是哈希捅,哈希捅的逻辑十分简单,就是不一样的链表和数组而已(个人认为)。但是千万不要绝的实现unordered_map与unordered_set很简单,就像做饭一样,我们每个人都可以做出挂面,但是最后吃进嘴的感觉,味道是不一样,大厨做出来的大概率会比一个做饭小白做的好吃,而unordered_map与unordered_set就像做饭一样,想实现哈希捅不难,但是封装接口是真的难。下面是代码:

//unordered_map头文件
#pragma once
#include"Hash.h"
namespace myunmap
{
	template<class K,class V>
	class myunm
	{
	public:
		struct map;
		typedef Hash::HashNode<pair<K,V>> node;
		typedef typename Hash::Iterator<K, pair<K,V>, map,pair<K,V>&, pair<K,V>*, Hash::HashFunc<K>> iterator;
		struct map
		{
			const K& operator()(const pair<K,V>& x)
			{
				return x.first;
			}
		};
		pair<iterator,bool> insert(const pair<K, V>& x)
		{
			return _Table.insert(x);
		}
		node* find(const K& x)
		{
			return _Table.find(x);
		}
		iterator begin()
		{
			return _Table.begin();
		}
		iterator end()
		{
			return _Table.end();
		}
		V& operator[](const K& data)
		{
			pair<iterator, bool> ret = _Table.insert(make_pair(data, V()));
			return ret.first->second;
		}
		
	private:
		Hash::HashTable<K,pair<K,V>,map,Hash::HashFunc<K>> _Table;
	};
}


//unordered_set头文件
#pragma once
#include "Hash.h"
namespace myunset
{
	template<class K>
	class myset
	{
	public:
		struct set;
		typedef Hash::HashNode<K> node;
		typedef typename Hash::Iterator<K, K, set, K&, K*, Hash::HashFunc<K>> iterator;
		struct set
		{
			const K& operator()(const K& x)
			{
				return x;
			}
		};
		pair<iterator,bool> insert(const K& x)
		{
			return _Table.insert(x);
		}
		node* find(const K& x)
		{
			return _Table.find(x);
		}
		iterator begin()
		{
			return _Table.begin();
		}
		iterator end()
		{
			return _Table.end();
		}
	private:
		Hash::HashTable<K,K,set,Hash::HashFunc<K>> _Table;
	};
}
//Hash.h头文件
#pragma once
#include<iostream>
#include<vector>
#include<string>
#include<map>
#include<set>
using namespace std;
namespace Hash
{
	template<class K>
	struct HashFunc;
	template<class T>
	struct HashNode;
	template<class K, class T, class KofT, class Hashf>
	class HashTable;
	template<class K ,class T,class KofT,  class ref, class ptr,class Hashf = Hash::HashFunc<K>>
	struct Iterator
	{
		typedef HashNode<T> node;
		typedef HashTable<K, T, KofT, Hashf> HT;
		node* ret;
		HT* _ht;
		Iterator(node* x,HT* ht)
			:ret(x)
			,_ht(ht)
		{}
		ref operator*()const
		{
			return ret->_data;
		}
		ptr operator->()const
		{
			return &ret->_data;
		}
		Iterator& operator++()
		{
			if (ret->_next)
				ret = ret->_next;
			else
			{
				KofT kot;
				Hashf hsf;
				size_t pp = hsf(kot(ret->_data)) % _ht->_table.size();
				for (size_t i = pp + 1; i < _ht->_table.size(); i++)
				{
					if (_ht->_table[i])
					{
						ret = _ht->_table[i];
						return *this;
					}
				}
				ret = nullptr;
			}
			return *this;
		}
		bool operator!=(const Iterator<K, T, KofT, ref, ptr, Hashf>& x)const
		{
			return ret != x.ret;
		}
	};
	template<class K>
	struct HashFunc
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
	template<>
	struct HashFunc<string>
	{
		size_t operator()(const string& key)
		{
			size_t val = 0;
			for (auto e : key)
			{
				val += e;
				val *= 131;
			}
			return val;
		}
	};
	template<class T>
	struct HashNode
	{
		T _data;
		HashNode<T>* _next;
		HashNode(const T& val)
			:_data(val)
			,_next(nullptr)
		{}
	};
	template<class K ,class T , class KofT , class Hashf = Hash::HashFunc<K>>
	class HashTable
	{
		typedef HashNode<T> node;
		template<class K, class T, class KofT, class ref, class ptr, class Hashf>
		friend struct Iterator;
		typedef Iterator<K, T, KofT,T&,T*,Hashf> iterator;

	public:
		HashTable()
		{
			_table.resize(3);
		}
		iterator begin()
		{
			for (size_t i = 0; i < _table.size(); i++)
			{
				node* cur = _table[i];
				if (cur)
					return iterator(cur, this);
			}
			return iterator(nullptr, this);
		}
		iterator end()
		{
			return iterator(nullptr, this);
		}
		pair<iterator,bool> insert(const T& x)
		{
			//去重
			if (find(_kot(x)))
				return make_pair(iterator(find(_kot(x)),this), false);
			if (_size != 0 && _size / _table.size() == 1)
			{
				vector<node*> tmp;
				tmp.resize(2 * _table.size(), nullptr);
				for (size_t i = 0; i < _table.size(); i++)
				{
					if (_table[i])
					{
						node* cur = _table[i];
						while (cur)
						{
							node* next = cur->_next;
							size_t ret = hsf(_kot(cur->_data))% tmp.size();
							cur->_next = tmp[ret];
							tmp[ret] = cur;
							cur = next;
						}
					}
				}
				_table.swap(tmp);
			}
			int ret = hsf(_kot(x)) % _table.size();
			node* cur = new node(x);
			cur->_next = _table[ret];
			_table[ret] = cur;
			_size++;
			return make_pair(iterator(cur, this), true);
		}
		node* find(const K& key)
		{
			if (_table.size() == 0)
				return nullptr;
			size_t ret = hsf(key) % _table.size();
			if (_table[ret])
			{
				node* cur = _table[ret];
				while (cur)
				{
					if (_kot(cur->_data) == key)
						return cur;
					cur = cur->_next;
				}
			}
			return nullptr;
		}
	private:
		vector<node*> _table;
		size_t _size = 0;
		KofT _kot;
		Hashf hsf;
	};
}
//测试代码
#include"Hash.h"
#include"unordered_map.h"
#include"unordered_set.h"
int main()
{
	/*Hash::HashTable<int> s;
	s.insert(1);
	s.insert(1);
	s.insert(1);
	s.insert(1);*/
	myunmap::myunm<int, int> s;
	vector<int> v = { 1,1,1,1,1,1,1,1 };
	//myunmap::myunm<int, int>::iterator sit = s.begin();
	for (auto e : v)
	{
		s[e]++;
	}
	for (auto& e : s)
		cout << e.first << endl;
	/*myunset::myset<int> h;
	h.insert(1);
	h.insert(2);
	h.insert(3);
	h.insert(4);
	h.insert(5);
	h.insert(6);
	h.insert(7);
	myunset::myset<int>::iterator sit = h.begin();
	while (sit != h.end())
	{
		cout << (*sit) << endl;
		++sit;
	}*/
	return 0;
}

怎么说,实现哈希捅的时候,前前后后大概用了一个小时左右,封装接口用了三个小时左右,封装代码的过程怎么说,难受,其实在封装其他的时候,感觉还可以,但是到迭代器的那一块,真的是感觉绕糊了,为什么说很绕呢?其实也怪我写的位置,在最开始,所以把下面所有的类都声明了一遍。所以导致看起来很乱。行了,说说怎么实现的吧。

其他地方就不说了,主要是迭代器那一块。

迭代器的模版传参数,我刚开始写的是K,K&,K*。但是这样写你会发现一个问题,那就是没有哈希捅的指针,而要实现迭代器,哈希捅的指针是必不可少的,所以,这就很矛盾。所以不可以这样设置参数。因为要用哈希捅的指针,所以哈希捅的四个模版参数不能少,其实如果没有啥的话,就可以实现了。但是我又加了两个参数,那就是ref,ptr。这样就可以方便一些。好了,哈希捅的指针问题解决之后,就相当于是迭代器中的复杂问题都解决了。但是下一个问题又来了,那就是operator[]怎么办?这个重载函数的参数应该是K,但是你要插入的话,像unordered_map的插入是pair啊,所以,此时就必须要在unordered_map的这个文件中,调用Hash文件中的插入,在传下去。

以上就是两个难点,虽然说起来简单,但是自己实现的话,我觉还是有点难度的,实现封装比实现哈希捅难多了,尤其是往下传这个过程,特别绕。我个人认为,map,set的封装都比unordered_map,unordered_set简单,主要是unordered_map,unordered_set这两个是要用哈希函数来映射的,所以必须要支持取余,但是如果是string呢?string的话就不用自己实现的了?所以此时需要一个仿函数。而map,set不需要,红黑树是我只要知道你是pair还是K就好了。所以只要一个仿函数就可以了。

最后,希望大家支持一下吧!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值