unordered_map set的模拟实现

本文详细介绍了使用C++实现哈希表的开散列处理冲突的方法,强调了迭代器在处理链表遍历中的关键作用。讨论了如何通过哈希函数对不同类型的键进行转换,特别是对字符串类型的特化处理。此外,还探讨了迭代器的设计,包括处理桶间切换的策略,以及在C++11中通过节点前后置指针优化迭代器的方法。文章还提到了哈希表的扩容策略,使用素数表降低冲突,并展示了如何在map和set中实例化模板。
摘要由CSDN通过智能技术生成

设计容器时,哈希注重的是如何处理哈希冲突,显然,开散列的处理方式要优于闭散列,因此使用开散列处理哈希冲突,模拟实现的本质是对迭代器如何处理,因为其他的接口都是很好完成的,但是迭代器如何形成遍历的方式呢?

在封装迭代器前,我们先思考一个问题,如何进行类型的不同调整,因为底层是用除留余数法进行映射的,但是除了整形,还有什么能取余呢,因此需要对不同的类型进行不同的转换,我们只是模拟实现,因此处理最多使用的string类,使用模板的特化,对string类进行特殊处理,这样将其转换成对应的整形,这里也要使用一下仿函数来进行特化的处理。

迭代器的封装一样,封装的是节点,所以遍历的方式是和链表一样,但是最主要的问题是,一个桶遍历完了后,如何切换到下一个桶~

我们给出的方案是,将哈希表也封装在迭代器中,然后使用哈希表来定位桶。但这样也无疑使结构变得复杂,因此c++11中使用的方式是,将节点增加两个指针,一个是前置,一个后置,让插入顺序为这个链表的顺序,因此同样的节点有着两个顺序的链接方式,依此来处理迭代器,但是这样在插入和删除方向要复杂一些。

当然任何的事物都有其优缺点,但是在实现的方向上,我们主要学习其实现的思想,更多的是这种高维度的泛型技术,使用一个哈希表,同时满足两种实现,高维度的泛型技术是必修课。对于迭代器的处理我们可以想不出来,但是对于泛型的高维度使用却是必须掌握的。

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


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

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

template<class K, class T, class KOfT, class Hash>
class HashTable;


template <class K,class T,class KOfT,class Hash>
struct __Hashiterator
{
	typedef  __Hashiterator<K, T, KOfT, Hash> Self;
	typedef HashTable<K, T, KOfT, Hash> HT;
	typedef HashNode<T> Node;
	Node* _node;
	HT* _pht;

	__Hashiterator(Node* node,HT* pht)
		:_node(node)
		,_pht(pht)
	{}

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

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

	bool operator!=(Node* node)
	{
		return _node != node;
	}

	bool operator==(Node* node)
	{
		return _node == node;
	}

	Self operator++()
	{
		if (_node->_next)
		{
			_node = _node->_next;
		}
		else
		{
			KOfT koft;
			size_t index = _pht->HashFunc(koft(_node->_data)) % 
							_pht->_tables.size();
			index++;

			while (index < _pht->_tables.size())
			{
				if (_pht->_tables[index])
				{
					_node = _pht->_tables[index];
					return *this;
				}
			}
			_node = nullptr;
		}

		return *this;
	}

};


template <class K>
struct _Hash
{
	const K& operator() (const K& key)
	{
		return key;
	}
};

template <>
struct _Hash <string>
{
	size_t operator() (const string& s)
	{
		size_t hash = 0;
		for (int i = 0; i < s.size(); i++)
		{
			hash *= 131;
			hash += s[i];
		}
	}
};

template<class K, class T, class KOfT ,class Hash>
class HashTable
{
	friend struct  __Hashiterator<K, T, KOfT, Hash>;
	typedef HashNode<T> Node;
	typedef __Hashiterator<K, T, KOfT, Hash> iterator;
public:
	iterator begin()
	{
		for (int i = 0; i < _tables.size(); i++)
		{
			if (_tables[i])
			{
				return iterator(_tables[i], this);
			}
		}

		return end();
	}

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


	~HashTable()
	{
		clear();
	}

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

			_tables[i] = nullptr;
		}
	}

	size_t HashFunc(const K& key)
	{
		Hash hash;
		return hash(key);
	}
	size_t GetPrime(size_t num)
	{
		const int PRIMECOUNT = 28;
		const size_t primeList[PRIMECOUNT] =
		{
		 53ul, 97ul, 193ul, 389ul, 769ul,
		 1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
		 49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
		 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
		 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
		 1610612741ul, 3221225473ul, 4294967291ul
		};


		for (size_t i = 0; i < PRIMECOUNT; i++)
		{
			if (primeList[i] > num)
			{
				return primeList[i];
			}
		}

		return primeList[28];
	}
	


	pair<iterator,bool> insert(const T& d)
	{
		KOfT koft;

		if (_num == _tables.size())
		{
			vector<Node*> newtables;
			size_t newsize = GetPrime(_tables.size());
			//size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
			newtables.resize(newsize);

			for (int i = 0; i < _tables.size(); i++)
			{
				Node* cur = _tables[i];

				while (cur)
				{
					Node* next = cur->_next;
					int index = HashFunc(koft(cur->_data)) % newtables.size();
					cur->_next = newtables[index];
					newtables[index] = cur;

					cur = next;
				}

				_tables[i] == nullptr;
			}
			_tables.swap(newtables);
		}




		size_t index = HashFunc(koft(d)) % _tables.size();

		Node* cur = _tables[index];

		while (cur)
		{
			if (koft(cur->_data) == koft(d))
			{
				return make_pair(iterator(cur, this), false);
			}
			else
			{
				cur = cur->_next;
			}
		}

		Node* newnode = new Node(d);
		newnode->_next = _tables[index];
		_tables[index] = newnode;

		_num++;

		return make_pair(iterator(newnode,this),true);

	}

	Node* find(const K& key)
	{
		KOfT koft;
		size_t index = key % _tables.size();
		Node* cur = _tables[index];

		while (cur)
		{
			if (koft(cur->_data) == key)
			{
				return cur;
			}
			else
			{
				cur = cur->_next;
			}
		}

		return nullptr;
	}

	bool erase(const K& key)
	{
		KOfT koft;
		size_t index = key % _tables.size();
		Node* cur = _tables[index];
		Node* prev = nullptr;

		while (cur)
		{
			if (koft(cur->_data) == key)
			{
				if (prev == nullptr)
				{
					_tables[index] = cur->_next;
				}
				else
				{
					prev->_next = cur->_next;
				}
				delete cur;
				_num--;
				return true;
			}
			else
			{
				prev = cur;
				cur = cur->_next;
			}
		}


		return false;
	}

private:
	vector<Node*> _tables;
	size_t _num = 0;
};

set

#pragma once
#include "hash.h"


namespace whc
{
	template <class K,class Hash = _Hash<K>>
	class unordered_set
	{
		struct SetKOfT
		{
			const K& operator() (const K& key)
			{
				return key;
			}

		};
	public:
		typedef typename HashTable<K, K, SetKOfT, Hash>::iterator iterator;

		iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}


		pair<iterator,bool> insert(const K& key)
		{
			return _ht.insert(key);
		}
	private:
		HashTable<K, K, SetKOfT,Hash> _ht;
	};
}

map

#pragma once
#include <iostream>
using namespace std;
#include "hash.h"


namespace whc
{
	template <class K,class V,class Hash = _Hash<K>>
	class unordered_map
	{
		struct MapKOfT
		{
			const K& key operator() (const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename HashTable<K, K, SetKOfT, Hash>::iterator iterator;

		iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}

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

		V& operator[] (const K& key)
		{
			pair<iterator, bool> ret = _ht.insert(make_pair(key, V()));

			return ret.first->second;
		}

	private:
		HashTable<K, pair<K, V>, MapKOfT,Hash> _ht;
	};
}

以上还有两点细节需要说明:
第一是对于扩容的方式,因为研究表明素数的哈希冲突较为小,所以使用一个素数表来进行扩容。
第二是在map和set中还未进行模板的实例化,因此使用typedef的时候,要使用后面实例化的模板,就得在前面用typename进行声明,来告诉编译器,后面会实例化的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值