【哈希表完整代码】模拟实现哈希表和unordered_set与unordered_map

目录

HashTable.h:

Test.cpp:

MyUnorderedSet.h:


HashTable.h:

#pragma once
#include
#include
#include//pair头文件
#include
#include

using namespace std;

namespace CLOSEHASH
{
	enum State
	{
		EMPTY, //空
		EXIST, //存在
		DELETE //删除
	};

	template
	struct HashData
	{
		T _data;
		State _state; //代表数据状态

		HashData()
			:_state(EMPTY)
			,_data(0)
		{}
	};

	template
	class HashTable
	{
		typedef HashData HashData;
	public:
		bool Insert(const T& data)
		{
			KeyOfT koft;
			//1、增容:第一次插入或者负载因子>=0.7就要增容
			if (_tables.capacity() == 0 || _num * 10 / _tables.capacity() == 7)
			{
				//A、增容——传统思路
				//vector newtables;
				//size_t newcapacity = _tables.capacity() == 0 ? 10 : _tables.capacity() * 2;
				//newtables.resize(newcapacity);//开空间+自动初始化为0
				把旧空间数据拷贝到新空间中
				//for (size_t i = 0; i < _tables.capacity(); ++i)
				//{
				//	if (_tables[i]._state == EXIST)
				//	{
				//		size_t index = koft(_tables[i]._data) % newtables.capacity();
				//		while (newtables[index]._state == EXIST)
				//		{
				//			index++;

				//			if (index == newtables.capacity())
				//			{
				//				index = 0;//走到尾了就要返回头找位置
				//			}
				//		}
				//		newtables[index] = _tables[i];
				//	}
				//}

				//_tables.swap(newtables);

				//B、增容——简便思路
				HashTable newht;
				size_t newcapacity = _tables.capacity() == 0 ? 10 : _tables.capacity() * 2;
				newht._tables.resize(newcapacity);

				for (size_t i = 0; i < _tables.capacity(); ++i)
				{
					if (_tables[i]._state == EXIST)
					{
						newht.Insert(_tables[i]._data);//把原哈希表中每个数据利用Insert都插入到新哈希表中
					}
				}

				_tables.swap(newht._tables);//交换两者的vector
			}

			//1、线性探测
			//size_t index = koft(data) % _tables.capacity();//计算出要映射的位置
			//while (_tables[index]._state == EXIST)
			//{
			//	if (koft(_tables[index]._data) == koft(data))
			//	{
			//		return false;//如果存在相同的数据
			//	}

			//	++index;
			//	if (index == _tables.capacity())
			//	{
			//		index = 0;
			//	}
			//}

			//2、二次探测
			size_t start = koft(data) % _tables.capacity();
			size_t index = start;
			int i = 1;
			while (_tables[index]._state == EXIST)
			{
				if (koft(_tables[index]._data) == koft(data))
				{
					return false;
				}

				index = start + i * i;
				++i;
				index %= _tables.capacity();
			}

			//插入数据
			_tables[index]._data = data;
			_tables[index]._state = EXIST;
			++_num;		//有效数据个数++
			
			return true;
		}

		HashData* Find(const K& key)
		{
			KeyOfT koft;
			size_t index = key % _tables.capacity();
			while(_tables[index]._state != EMPTY)
			{//只要是存在和删除状态就要持续往下找
				if (koft(_tables[index]._data) == key)
				{
					if (_tables[index]._state == EXIST)
						return &_tables[index];//值相等且为存在状态
					else
						return nullptr;//值相等但为删除状态,说明被删除了
				}

				++index;//没找到继续往后找
				index %= _tables.capacity();
			}

			return nullptr;
		}

		bool Erase(const K& key)
		{
			HashData* ret = Find(key);
			if (ret)
			{
				ret->_state = DELETE;//用状态代表删除状态
				--_num; //--有效元素个数
				return true;
			}
			else
			{
				return false;
			}
		}

	private:
		vector _tables;
		size_t _num = 0;	//表中存了多少个有效个数,不等于容量
	};
}

namespace OPENHASH
{
	template
	struct HashNode
	{
		T _data;
		HashNode* _next;

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

	//前置声明:为了让哈希表的迭代器能用哈希表
	template
	class HashTable;		

	template
	struct __HashTableIterator
	{
		typedef __HashTableIterator Self;
		typedef HashTable HT;
		typedef HashNode Node;
		Node* _node;//迭代器中存的是节点指针
		HT* _pht;//对象的指针

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

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

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

		Self operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				// 如果一个桶走完了,要往下找到下一个桶继续遍历
				KeyOfT koft;
				//先计算我当前是在哪个桶
				size_t i = _pht->HashFunc(koft(_node->_data)) % _pht->_tables.capacity();
				++i;//下一个桶
				for (; i < _pht->_tables.capacity(); ++i)
				{	//找不为空的桶
					Node* cur = _pht->_tables[i];
					if (cur)
					{	//如果这个桶不为空
						_node = cur;
						return *this;
					}
				}

				_node = nullptr;//如果没有找到有数据的桶,则指针置为空,与end()相符
				return *this;
			}
		}

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

	template
	struct _Hash
	{
		const K& operator()(const K& key)
		{
			return key;//可以取模的直接返回即可
		}
	};

	//特化
	template<>
	struct _Hash
	{
		size_t operator()(const string& key)
		{
			//运用BKDR Hash算法
			size_t hash = 0;
			for (size_t i = 0; i < key.size(); ++i)
			{
				hash *= 131; //BKDR
				hash += key[i];//字符串中每个字母ASCII码值相加
			}

			return hash;
		}
	};

	template
	class HashTable
	{
		typedef HashNode Node;
	public:
		friend struct __HashTableIterator;//因为迭代器要访问哈希表的私有成员_tables
		typedef __HashTableIterator iterator;

		//begin()返回第一个不为空的桶的第一个节点
		iterator begin()
		{
			for (size_t i = 0; i < _tables.capacity(); ++i)
			{
				if (_tables[i])
				{
					return iterator(_tables[i], this);//找到了则构造匿名对象返回
				}
			}

			return end();//每个桶中都没找到则返回end()
		}

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

		size_t HashFunc(const K& key)
		{
			Hash hash;
			return hash(key);//利用仿函数把key值转为整形
		}

		size_t GetNextPrime(size_t num)
		{
			const int PRIMECOUNT = 28;
			static 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[PRIMECOUNT - 1];//如果num比我表中所有数据都大,默认返回表中最后一个
		}

		pair Insert(const T& data)
		{
			KeyOfT koft;
			//1、判断是否增容
			if (_tables.capacity() == _num)
			{	//开散列的实现平衡因子为1就增容且第一次插入也会增容
				//size_t newcapacity = _tables.capacity() == 0 ? 10 : _tables.capacity() * 2;
				size_t newcapacity = GetNextPrime(_tables.capacity());
				vector newtables;
				newtables.resize(newcapacity, nullptr);//给新的vector开新空间+初始化
				//重新计算旧表的数据在新表中的映射位置
				for (size_t i = 0; i < _tables.capacity(); ++i)
				{	//如果是第一次的增容不会进for循环的,故不用担忧表的初始数据是否为nullptr
					//哈希表中每一个桶都是一个单链表,故考察单链表的头插
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;
						size_t index = HashFunc(koft(cur->_data)) % newtables.capacity();//重新计算映射位置
						//头插
						cur->_next = newtables[index];
						newtables[index] = cur;
						 
						cur = next;
					}
					_tables[i] = nullptr;//映射到新表后置为空
				}

				_tables.swap(newtables);//新旧表的vector交换

			}

			size_t index = HashFunc(koft(data)) % _tables.capacity();//计算新的映射位置

			//1、先查找这个元素是否在哈希表中
			Node* cur = _tables[index];//知道映射位置就能确定是哪个桶
			while (cur)
			{
				if (koft(cur->_data) == koft(data))
					return make_pair(iterator(cur, this), false);//找到相同数据则插入失败
				else
					cur = cur->_next;
			}

			//2、头插到这个桶中
			Node* newnode = new Node(data);//开辟新节点
			//头插
			newnode->_next = _tables[index];
			_tables[index] = newnode;

			++_num;//哈希表中有效元素个数++
			return make_pair(iterator(newnode, this), false);
		}


		Node* Find(const K& key)
		{
			KeyOfT koft;
			size_t index = HashFunc(key) % _tables.capacity();//先计算映射位置
			Node* cur = _tables[index];
			while (cur)
			{
				if (koft(cur->_data) == key)
					return cur;
				else
					cur = cur->_next;
			}

			return nullptr;//走遍桶都没找到则返回空
		}

		bool Erase(const K& key)
		{
			assert(_tables.capacity() > 0);//有空间才能删

			KeyOfT koft;
			size_t index = HashFunc(key) % _tables.capacity();
			Node* cur = _tables[index];
			Node* prev = nullptr;//记录cur的前一位置

			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;//要删除数据根本不存在
		}

		~HashTable()
		{
			Clear();
			//vector不用我们释放,因为它是自定义类型,哈希表要清理时,vector也会自动清理
		}

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

				_tables[i] = nullptr;//清空完数据后置为nullptr
			}
		}

	private:
		vector _tables; //使用vector一定要包含std库,不然使用不了
		size_t _num = 0;      //有效元素的个数,不是容量
	};

}

Test.cpp:

#include"HashTable.h"
#include"MyUnorderedMap.h"
#include"MyUnorderedSet.h"

template
struct SetKeyOfT
{
	const K& operator()(const K& key)
	{
		return key;
	}
};

void TestCloseHash()
{
	CLOSEHASH::HashTable> ht;
	//CLOSEHASH::HashTable> ht2;

	ht.Insert(2);
	ht.Insert(4);
	ht.Insert(14);
	ht.Insert(24);
	ht.Insert(26);
	ht.Insert(16);

	ht.Erase(14);
	ht.Erase(2);

	CLOSEHASH::HashData* data = ht.Find(4);
}

void TestOpenHash1()
{
	using OPENHASH::_Hash;

	OPENHASH::HashTable, _Hash> ht;
	//OPENHASH::HashTable> ht2;

	ht.Insert(2);
	ht.Insert(4);
	ht.Insert(14);
	ht.Insert(24);
	ht.Insert(26);
	ht.Insert(16);

	ht.Erase(14);
	ht.Erase(2);

	OPENHASH::HashNode* Node = ht.Find(4);

	/*ht2.Insert(make_pair(1,1));
	ht2.Insert(4);
	ht2.Insert(14);
	ht2.Insert(24);
	ht2.Insert(26);
	ht2.Insert(16);

	ht2.Erase(14);
	ht2.Erase(2);

	HashNode* Node2 = ht2.Find(4);*/

}

void TestOpenHash2()
{
	using OPENHASH::_Hash;

	OPENHASH::HashTable,_Hash> ht;
	ht.Insert("sort");
	ht.Insert("futile");
	ht.Insert("plight");

	cout << ht.HashFunc("abcd") << endl;
	cout << ht.HashFunc("aadd") << endl;

}

int main()
{
	//TestCloseHash();
	//TestOpenHash1();
	//TestOpenHash2();
	//mz::test_unordered_set();
	mz::test_unordered_map();


	return 0;
}

MyUnorderedSet.h:

#pragma once
#include"HashTable.h"
using namespace OPENHASH;

namespace mz
{
	using OPENHASH::_Hash;
	template>
	class unordered_set
	{
		struct SetKOfT
		{
			const K& operator()(const K& k)
			{
				return k;
			}
		};

	public:
		//加typename是告诉编译器这是个类型,你先让我过,等实例化了再去找
		//因为模板没实例化它是不接受你用模板里面的一个类型,故要用typename 

		typedef typename HashTable::iterator iterator;
		iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}
		pair insert(const K& k)
		{
			return _ht.Insert(k);
		}
	private:
		HashTable _ht;
	};

	void test_unordered_set()
	{
		unordered_set s;
		s.insert(1);
		s.insert(5);
		s.insert(4);
		s.insert(2);

		unordered_set::iterator it = s.begin();
		while (it != s.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}
}

MyUnorderedMap.h:

#pragma once
#include"HashTable.h"
using namespace OPENHASH;

namespace mz
{
	using OPENHASH::_Hash;
	template>//一般模板参数都是由上一层来控制的
	class unordered_map
	{
		struct MapKOfT 
		{
			const K& operator()(const pair& kv)
			{
				return kv.first;
			}
		};

	public:

		typedef typename HashTable, MapKOfT, Hash>::iterator iterator;
		iterator begin()
		{
			return _ht.begin();
		}
		
		iterator end()
		{
			return _ht.end();
		}

		pair insert(const pair& kv)
		{
			return _ht.Insert(kv);
		}

		V& operator[](const K& key)
		{
			pair ret = _ht.Insert(make_pair(key, V()));
			return ret.first->second;
		}

	private:
		HashTable, MapKOfT, Hash> _ht;//底层是个哈希表
	};

	void test_unordered_map()
	{
		unordered_map dict;
		dict.insert(make_pair("factual", "真实的"));
		dict.insert(make_pair("fringe", "侵犯"));
		dict.insert(make_pair("intermittent", "间歇的"));
		dict["prerequisite"] = "先决条件";
		dict["reduce to"] = "处于";

		//unordered_map::iterator it = dict.begin();
		auto it = dict.begin();
		while (it != dict.end())
		{
			cout << it->first << ":" << it->second << endl;
			++it;
		}
		cout << endl;

	}
}

你可能感兴趣的:(【C++】项目,【数据结构】知识篇+代码讲解,散列表,数据结构,算法,c++)