【C++进阶】使用一个哈希桶封装出unordered_map和uordered_set

文章目录

  • 改造原来的哈希桶
  • 实现时要注意的问题
    • 1.unordered_map和unordered_set的模版参数
    • 2.KeyOfT仿函数
    • 3.string类型无法取模问题
  • 哈希桶的迭代器实现
    • 1.迭代器的结构
    • 2.迭代器++
  • 代码实现
    • 1.unordered_map
    • 2.unordered_set

改造原来的哈希桶

由于要使用一个哈希桶封装出unordered_map和uordered_set,所以要对哈希桶的模版参数进行改造,由于unordered_map是kv模型,而uordered_set是k模型,所以必须在实现哈希桶的时候提供模版,在封装uordered_set和unordered_map时传入不同的模版参数。
还要要修改的是,如果同时封装出,在获得key值时,unordered_map 和 unordered_set的key是不同的,所以必须传入不同仿函数来获得key值,为了后边在可以进行key比较。

namespace HashBucket
{
	template
	class HashNode
	{
	public:
		typedef HashNode Node;
		HashNode(const T& data)
			:_data(data)
			,_next(nullptr)
		{}
		T _data;
		Node* _next;
	};
	template
	class HashTable;
		
	template
	class HashTable
	{
	public:
		typedef HashNode Node;
		typedef iterator iterator;
		
		iterator begin()
		{
			int i = 0;
			while (!_tables[i])
			{
				i++;
			}
			return iterator(_tables[i], this);
		}
		iterator end()
		{
			return iterator(nullptr, 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;
			}
		}

		pair insert(const T& data)
		{
			Hash hash;
			KeyOfT kot;
			auto ret = find(kot(data));
			if (ret!=end())
			{
				return make_pair(ret, false);
			}
			if (_size == _tables.size())
			{
				size_t NewSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				vector NewTables;
				NewTables.resize(NewSize, nullptr);

				for (size_t i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					if (cur)
					{
						Node* next = cur->_next;

						size_t hashi = hash(kot(cur->_data)) % _tables.size();
						cur->_next = NewTables[hashi];
						NewTables[hashi] = cur;

						cur = next;
					}
					_tables[i] = nullptr;
				}
				_tables.swap(NewTables);
			}

			size_t hashi = hash(kot(data)) % _tables.size();
			Node* NewNode = new Node(data);

			NewNode->_next = _tables[hashi];
			_tables[hashi] = NewNode;

			_size++;

			return make_pair(iterator(NewNode,this), true);
		}
				
		iterator find(const K& key)
		{
			Hash hash;
			KeyOfT kot;
			if (_tables.size() == 0)
			{
				return end();
			}
			size_t hashi = hash(key) % _tables.size();
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(cur,this);
				}
				cur = cur->_next;
			}
			return end();
		}

		bool earse(const K& key)
		{
			Hash hash;
			KeyOfT kot;
			size_t hashi = hash(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_kv) == key)
				{
					if (cur == _tables[hashi])
					{
						_tables[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}
					delete cur;
					--_size;

					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}

	private:
		vector _tables;
		size_t _size = 0;
	};

实现时要注意的问题

1.unordered_map和unordered_set的模版参数

unordered_map中的value值传入pair,而unordered_set中的value值和key值相同。
【C++进阶】使用一个哈希桶封装出unordered_map和uordered_set_第1张图片

2.KeyOfT仿函数

由于unordered_map的得到key值的方法和unordered_set获得可以值的方法不同,所以必须传入KeyOfT仿函数:

		struct MapKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
		struct MapKeyOfT
		{
			const K& operator()(const pair& kv)
			{
				return kv.first;
			}
		};

【C++进阶】使用一个哈希桶封装出unordered_map和uordered_set_第2张图片

3.string类型无法取模问题

由于在使用哈希函数为除留余数法时,会对key值进行取模,但是可能有的类型不支持取模,例如string,所以我们同样使用模版来解决,此时使用类模板来解决,同时对string类型进行类模板的特化:

template
class HashFunc
{
public:
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};
template<>
class HashFunc
{
public:
	size_t operator()(const string& k)
	{
		size_t val = 0;
		for (auto e : k)
		{
			val *= 131;
			val += e;
		}
		return val;
	}
};

哈希桶的迭代器实现

实现unordered_map和unordered_set的迭代器,即实现哈希桶的迭代器就可以了,最后对哈希桶的迭代器进行封装:

1.迭代器的结构

	struct iterator
	{
		typedef HashTable HT;
		typedef HashNode Node;

		HT* _Pht;
		Node* _node;
	};

迭代器的成员变量为一个节点的指针,另外还需要哈希表的地址。

2.迭代器++

例如下边这个哈希桶:
【C++进阶】使用一个哈希桶封装出unordered_map和uordered_set_第3张图片
使用迭代器后遍历顺序是这样的。
【C++进阶】使用一个哈希桶封装出unordered_map和uordered_set_第4张图片
迭代器++的规则:
如果哈希表中某一处为空,就去拿表中节点,根据单链表的方式,向下遍历,直到遍历到单链表为空,为空后,继续在哈希表中查找不为空处,继续根据单链表的方式向下遍历。

代码实现

1.unordered_map

#pragma once
#include"hash.h"


namespace tmt
{

	template>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair& kv)
			{
				return kv.first;
			}
		};
		typedef typename HashBucket::HashTable,Hash,MapKeyOfT>::iterator iterator;
	public:
		bool insert(const pair& kv)
		{
			return _HT.insert(kv);
		}
		iterator begin()
		{
			return _HT.begin();
		}
		iterator end()
		{
			return _HT.end();
		}
		V& operator[](const K& key)
		{
			pair ret = _HT.insert(make_pair(key,V()));
			return ret.first->second;
		}
	private:
		HashBucket::HashTable, Hash ,MapKeyOfT> _HT;
	};
}

2.unordered_set

namespace tmt
{
	template>
	class unordered_set
	{
		struct MapKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
		typedef typename HashBucket::HashTable::iterator iterator;
	public:
		bool insert(const K& key)
		{
			return _HT.insert(key);
		}
		iterator begin()
		{
			return _HT.begin();
		}
		iterator end()
		{
			return _HT.end();
		}
		V& operator[](const K& key)
		{
			pair ret = _HT.insert(make_pair(key, V()));
			return ret.first->second;
		}
	private:
		HashBucket::HashTable _HT;
	};
}

你可能感兴趣的:(C++进阶,哈希算法,c++,散列表)