【C++】用哈希表封装unordered_XX

目录

[1. 复用哈希表](#1. 复用哈希表)

[2. Iterator和ConstIterator的实现](#2. Iterator和ConstIterator的实现)

[3. 实现unordered_set](#3. 实现unordered_set)

[4. 实现unordered_map](#4. 实现unordered_map)

[5. 完整代码](#5. 完整代码)


本篇文章我们要实现unodered_map和unodered_set,基本的大致思路其实和前面map和set的封装差不多:【C++】map和set的封装-CSDN博客

所以相同的内容我就不再赘述了,本篇主要就是展示一下大致思路,代码部分偏多,大家可以一起试试实现一下哦!

1. 复用哈希表

我们在前面的文章中就已经实现过哈希表了:【C++】哈希表的实现-CSDN博客

本篇文章中实现unordered_map和unordered_set复用的是用链地址法实现的哈希表(即哈希桶)

在此基础上稍作改动:

cpp 复制代码
//解决有些key不能取模的问题
template<class K>
struct HashKey
{
	//能转化成size_t的,就直接转换
	size_t operator()(const K& k)
	{
		return size_t(k);
	}
};

//特化
template<>
struct HashKey<string>
{
	size_t operator()(const string& s)
	{
		size_t ret = 0;
		for (auto e : s)
		{
			ret *= 31;
			ret += e;
		}
		return ret;
	}

};
//链地址法
namespace hash_bucket
{
	template<class T>
	struct HashNode
	{
		T _data;
		HashNode* _next;

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

	

	template<class K,class T,class KeyOfT,class Hash = HashKey<K>>
	class HashTable {
		public:
		typedef HashNode<T> Node;
		
		
		HashTable()
		{
			_tables.resize(__stl_next_prime(0),nullptr);

		}
		~HashTable()
		{
			for (int i = 0; i < _tables.size(); i++)
			{
				while (_tables[i])
				{
					Node* del = _tables[i];
					_tables[i] = _tables[i]->_next;
					delete del;
					del = nullptr;
				}
			}
			_size = 0;
		}

		bool Insert(const T& data)
		{
			KeyOfT kot;
			Hash h;
			if (Find(kot(data)))
				return false;
			
			//负载因子大于0.7------扩容
			if ((double)_size / (double)_tables.size() > 0.7)
			{
				vector<Node*> newHT;
				newHT.resize(__stl_next_prime((unsigned long)_tables.size() + 1), nullptr);
				for (int i = 0; i < _tables.size(); i++)
				{
					while(_tables[i])
					{
						size_t hash0 = h(kot(_tables[i]->_data)) % newHT.size();
						Node* newnode = _tables[i];
						_tables[i] = newnode->_next;
						newnode->_next = newHT[hash0];
						newHT[hash0] = newnode;
					}
				}
				newHT.swap(_tables);
			}
			Node* newnode = new Node(data);
			size_t hash0 = h(kot(data)) % _tables.size();
			//头插
			newnode->_next = _tables[hash0];
			_tables[hash0] = newnode;
			_size++;
			return true;
		}
		Node* Find(const K& key)
		{
			Hash h;
			KeyOfT kot;
			size_t hash0 = h(key) % _tables.size();
			for (int i = 0; i < _tables.size(); i++)
			{
				Node* cur = _tables[i];
				while (cur)
				{
					if (kot(cur->_data) == key)
					{
						return cur;
					}
					cur = cur->_next;
				}
			}
			return nullptr;
		}

		bool Erase(const K& key)
		{
			Hash h;
			KeyOfT kot;
			size_t hash0 = h(key) % _tables.size();
			for (int i = 0; i < _tables.size(); i++)
			{
				Node* cur = _tables[i];
				Node* prev = nullptr;
				while (cur)
				{
					if (kot(cur->_data) == key)
					{
						if (prev == nullptr)
							_tables[i] = cur->_next;
						else
						{
							prev->_next = cur->_next;
						}
						delete cur;
						cur = nullptr;
						_size--;
						return true;
					}
					prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}
	private:
		inline unsigned long __stl_next_prime(unsigned long n)
		{
			// Note: assumes long is at least 32 bits.
			static const int __stl_num_primes = 28;
			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
			};
			const unsigned long* first = __stl_prime_list;
			const unsigned long* last = __stl_prime_list + __stl_num_primes;
			const unsigned long* pos = lower_bound(first, last, n);
			return pos == last ? *(last - 1) : *pos;
		}

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

2. Iterator和ConstIterator的实现

cpp 复制代码
template<class K, class T, class KeyOfT, class Hash>
class HashTable;

template<class K,class T,class Ref,class Ptr,class KeyOfT,class Hash>
struct HTIterator
{
	typedef HashNode<T> Node;
	typedef HashTable<K, T, KeyOfT, Hash> HT;
	typedef HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;
	Node* _node;
	const HT* _ht;

	HTIterator(Node* node,const HT* ht)
		:_node(node)
		,_ht(ht)
	{}

	Ref operator*()
	{
		return _node->_data;
	}
	Ptr operator->()
	{
		return &_node->_data;
	}
	bool operator==(const Self& s)
	{
		return _node == s._node;
	}
	bool operator!=(const Self& s)
	{
		return _node != s._node;
	}
	Self& operator++()
	{
		if (_node->_next)
		{
			_node = _node->_next;
		}
		else
		{
			KeyOfT kot;
			Hash h;
			size_t hashi = h(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 Hash = HashKey<K>>
class HashTable {
	// 友元声明
	template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
	friend struct HTIterator;

public:
	typedef HashNode<T> Node;
	typedef HTIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;
	typedef HTIterator<K, T, const T&, const T*, KeyOfT, Hash> ConstIterator;
	Iterator Begin()
	{
		size_t hashi = 0;
		while (!_tables[hashi])
		{
			hashi++;
		}
		return Iterator(_tables[hashi], this);
	}
	Iterator End()
	{
		return Iterator(nullptr, this);
	}
	ConstIterator Begin()const
	{
		size_t hashi = 0;
		while (!_tables[hashi])
		{
			hashi++;
		}
		return ConstIterator(_tables[hashi], this);
	}
	ConstIterator End()const
	{
		return ConstIterator(nullptr, this);
	}

    //...
};

3. 实现unordered_set

完整代码:

cpp 复制代码
#include"Hash.h"

namespace yfr
{
	template<class K,class Hash = HashKey<K>>
	class unodered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		//typedef typename hash_bucket::HashTable<K, K, SetKeyOfT, Hash>::Iterator Iterator;
		//typedef typename hash_bucket::HashTable<K, K, SetKeyOfT, Hash>::ConstIterator ConstIterator;
		using Iterator = typename hash_bucket::HashTable <K, K, SetKeyOfT, Hash>::Iterator;
		using ConstIterator = typename hash_bucket::HashTable <K, K, SetKeyOfT, Hash>::ConstIterator;

		Iterator begin()
		{
			return _ht.Begin();
		}
		Iterator end()
		{
			return _ht.End();
		}
		ConstIterator begin() const
		{
			return _ht.Begin();
		}
		ConstIterator end() const
		{
			return _ht.End();
		}

		bool insert(const K&key)
		{
			return _ht.Insert(key);
		}
		hash_bucket::HashNode<K>* find(const K& key)
		{
			return _ht.Find(key);
		}
		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}
	private:
		hash_bucket::HashTable<K, K, SetKeyOfT,Hash> _ht;
	};
}

4. 实现unordered_map

完整代码:

cpp 复制代码
namespace yfr
{
	template<class K,class V,class Hash= HashKey<K>>
	class undered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT, Hash>::Iterator Iterator;
		typedef typename hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT, Hash>::ConstIterator ConstIterator;

		Iterator begin()
		{
			return _ht.Begin();
		}
		Iterator end()
		{
			return _ht.End();
		}
		ConstIterator begin() const
		{
			return _ht.Begin();
		}
		ConstIterator end() const
		{
			return _ht.End();
		}

		bool insert(const pair<K, V>& p)
		{
			return _ht.Insert(p);
		}
		hash_bucket::HashNode<pair<K,V>>* find(const K& key)
		{
			return _ht.Find(key);
		}
		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}
	private:
		hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT, Hash> _ht;
	};
}

5. 完整代码

各位可以去这里看完整代码:unodered_xxx/unodered_xxx/Hash.h · 大白同学/C++进阶学习 - Gitee.com

相关推荐
The Chosen One9852 分钟前
红黑树下探玄机:C++ map&multimap 的幕后之旅
开发语言·c++
汤永红10 分钟前
week5-[字符数组]查找
c++·算法·信睡奥赛
Evand J13 分钟前
【MATLAB例程】水下机器人长基线(LBL)定位,用于三维轨迹,使用EKF滤波,融合LBL和IMU,4个锚点(长基线基站数=4),附下载链接
开发语言·matlab·机器人
林内克思14 分钟前
inline内联函数
java·开发语言·算法
博睿谷IT99_16 分钟前
OSPF 的工作过程、Router ID 机制、报文结构
开发语言·网络·华为·智能路由器·网络工程师·华为认证·数据通信
piikee30 分钟前
php内存缓存插件yac的安装配置--平替apcu,多进程共享内存
开发语言·缓存·php·yac·php扩展·php内存缓存·apcu平替
快乐的划水a32 分钟前
中介者模式及优化
c++·设计模式·中介者模式
多吃蔬菜!!!38 分钟前
VsCode 上的Opencv(C++)环境配置(Linux)
开发语言·c++
澡点睡觉1 小时前
【golang长途旅行第32站】反射
开发语言·后端·golang
咔咔咔的1 小时前
1277. 统计全为 1 的正方形子矩阵
c++