C++进阶--哈希表模拟实现unordered_set和unordered_map

哈希表模拟实现unordered_set和unordered_map

  • 一、定义哈希表的结点结构
  • 二、定义哈希表的迭代器
  • 三、定义哈希表的结构
    • 3.1 begin()和end()的实现
    • 3.2 默认成员函数的实现
      • 3.2.1 构造函数的实现
      • 3.2.2 拷贝构造函数的实现(深拷贝)
      • 3.2.3 赋值运算符重载函数的实现(现代写法)
      • 3.2.4 析构函数的实现
  • 四、实现哈希表的相关接口
    • 4.1 查找结点
    • 4.2 插入结点
    • 4.3 删除结点
  • 五、针对除留余数法的取模问题
  • 六、unordered_set的模拟实现
  • 七、unordered_map的模拟实现
  • 八、完整代码
    • 8.1 HashTable.h
    • 8.2 UnorderedSet.h
    • 8.3 UnorderedMap.h
    • 8.4 test.cpp

一、定义哈希表的结点结构

  这里直接拿自己写的开散列(原先是KV模型)改造下,使其既可兼容K模型,也可兼容KV模型:
哈希表里面具体存的是什么类型的元素,是由模板参数T来决定

  • 如果是unordered_set,传给T就是Key
  • 如果是unordered_map,传给T的就是pair

哈希表结点结构

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

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

二、定义哈希表的迭代器

迭代器的结构:
注意:哈希表迭代器封装的是结点指针哈希表指针,因为要在哈希表中找下一个桶,所以要用到哈希表指针

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 __HashIterator
	{
		typedef HashNode<T> Node;
		/*template>
		class HashTable*/
		typedef HashTable<K, T, KeyOfT, Hash> HT;
		typedef __HashIterator < K, T,Ref,Ptr,KeyOfT, Hash> Self;
		typedef __HashIterator < K, T,T&,T*,KeyOfT, Hash> Iterator;

		Node* _node;
		const HT* _ht;
};

   在构造正向迭代器时,我们不仅需要对应哈希结点的指针,还需要该哈希结点所在哈希表的地址。

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

__HashIterator(const Iterator&  it)
	:_node(it._node)
	, _ht(it._ht)
{}

   当对正向迭代器进行解引用操作时,我们直接返回对应结点数据的引用即可

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

   当对正向迭代器进行->操作时,我们直接返回对应结点数据的地址即可

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

   当我们需要比较两个迭代器是否相等时,只需要判断这两个迭代器所封装的结点是否是同一个即可。

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

++运算符重载函数的实现逻辑并不是很难,我们只需要知道如何找到当前结点的下一个结点即可。

  • 若当前结点不是当前哈希桶中的最后一个结点,则++后走到当前哈希桶的下一个结点。
  • 若当前结点是当前哈希桶的最后一个结点,则++后走到下一个非空哈希桶的第一个结点。
Self& operator++()
		{
			if (_node->_next != nullptr)
			{
				_node = _node->_next;
			}
			else
			{
				//找下一个不为空的桶
				KeyOfT kot;
				Hash hash;
				//算出我当前的桶位置
				size_t hashi = hash(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;
		}

三、定义哈希表的结构

   一个数组,数组中存储着各个链表头结点的地址。

注意
这里有一个私有成员访问的问题,我们在哈希表迭代器中封装了一个哈希表指针,当一个桶遍历完成时,用来在哈希表中找下一个桶,但哈希表_tables是私有成员,无法访问,所以需要将迭代器类模板声明为友元

template<class K,class T,class KeyOfT,class Hash=HashFunc<K>>
	class HashTable
	{
		template<class K,class T,class Ref,class Ptr,class KeyOfT,class Hash>
		friend struct __HashIterator;

		typedef HashNode<T> Node;
	public:
		typedef __HashIterator<K, T, T&,T*,KeyOfT, Hash> iterator;
		typedef __HashIterator<K, T, const T&,const T*,KeyOfT, Hash> const_iterator;
		private:
		vector<Node*> _tables;   //指针数组
		size_t _n=0;    //存储有效数据个数
	};

3.1 begin()和end()的实现

注意:this指针指向当前调用begin()成员函数的哈希表对象

  • begin函数:返回哈希表中第一个非空哈希桶中的第一个结点的正向迭代器。
  • end函数:返回空指针的正向迭代器。
iterator begin()
		{
			Node* cur = nullptr;
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				cur = _tables[i];
				if (cur)
				{
					break;
				}
			}

			return iterator(cur, this);
		}

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


		const_iterator begin() const
		{
			Node* cur = nullptr;
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				cur = _tables[i];
				if (cur)
				{
					break;
				}
			}

			return const_iterator(cur, this);
		}

		const_iterator end() const
		{
			return const_iterator(nullptr, this);
		}

3.2 默认成员函数的实现

3.2.1 构造函数的实现

   哈希表中有两个成员变量,当我们实例化一个对象时:

  • _table会自动调用vector的默认构造函数进行初始化
  • _n会根据我们所给的缺省值被设置为0
vector<Node*> _table; //哈希表
size_t _n = 0; //哈希表中的有效元素个数

   因此我们不需要编写构造函数,使用默认生成的构造函数就足够了,但是由于我们后面需要编写拷贝构造函数,编写了拷贝构造函数后,默认的构造函数就不会生成了,此时我们需要使用default关键字显示指定生成默认构造函数。

//构造函数
HashTable() = default; //显示指定生成默认构造函数

3.2.2 拷贝构造函数的实现(深拷贝)

   哈希表在拷贝时需要进行深拷贝,否则拷贝出来的哈希表和原哈希表中存储的都是同一批结点。
哈希表的拷贝函数实现逻辑如下:

1.将哈希表的大小调整为ht._table的大小
2.将ht._table每个桶当中的结点一个个拷贝到自己的哈希表中
3.更改哈希表当中的有效数据个数

//拷贝构造函数
HashTable(const HashTable& ht)
{
	//1、将哈希表的大小调整为ht._table的大小
	_table.resize(ht._table.size());
	//2、将ht._table每个桶当中的结点一个个拷贝到自己的哈希表中(深拷贝)
	for (size_t i = 0; i < ht._table.size(); i++)
	{
		if (ht._table[i]) //桶不为空
		{
			Node* cur = ht._table[i];
			while (cur) //将该桶的结点取完为止
			{
				Node* copy = new Node(cur->_data); //创建拷贝结点
				//将拷贝结点头插到当前桶
				copy->_next = _table[i];
				_table[i] = copy;
				cur = cur->_next; //取下一个待拷贝结点
			}
		}
	}
	//3、更改哈希表当中的有效数据个数
	_n = ht._n;
}

3.2.3 赋值运算符重载函数的实现(现代写法)

   实现赋值运算符重载函数时,可以通过参数间接调用拷贝构造函数,之后将拷贝构造出来的哈希表和当前哈希表的两个成员变量分别进行交换即可,当赋值运算符重载函数调用结束后,拷贝构造出来的哈希表会因为出了作用域而被自动析构,此时原哈希表之前的数据也就顺势被释放了。

//赋值运算符重载函数
HashTable& operator=(HashTable ht)
{
	//交换哈希表中两个成员变量的数据
	_table.swap(ht._table);
	swap(_n, ht._n);

	return *this; //支持连续赋值
}

3.2.4 析构函数的实现

   因为哈希表当中存储的结点都是new出来的,因此在哈希表被析构时必须进行结点的释放。在析构哈希表时我们只需要一次取出非空的哈希桶,遍历哈希桶当中的结点并进行释放即可。

~HashTable()
		{
			for (auto& cur : _tables)
			{
				while (cur)
				{
					Node* next = cur->_next;
					delete cur;
					cur = next;
				}
				cur = nullptr;
			}
		}

四、实现哈希表的相关接口

4.1 查找结点

   查找结点接口中,需要获取不同类型元素关键码key,因为元素间的比较是通过key值来比较的,以及通过元素关键码key取模计算出哈希位置。
所以需要借助两个仿函数类:

  • 仿函数类KeyOfT,获取不同类型的data中的关键码key值(如果data是key就取key,如果是pair就取first)
  • 仿函数类Hash,有些元素关键码key的类型不能直接取模,需要将其转换成可以取模的size_t类型
iterator Find(const K& key)
		{
			if (_tables.size() == 0)
				return end();

			KeyOfT kot;
			Hash hash;
			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();
		}

4.2 插入结点

   插入结点接口中,需要获取不同类型元素关键码key,因为元素间的比较是通过key值来比较的,以及通过元素关键码key取模计算出哈希位置。
所以需要借助两个仿函数类:

  • 仿函数类KeyOfT,获取不同类型的data中的关键码key值(如果data是key就取key,如果是pair就取first)
  • 仿函数类Hash,有些元素关键码key的类型不能直接取模,需要将其转换成可以取模的size_t类型

函数的返回值为pair类型对象:

  • 目的是为了在unordered_set和unordered_map中模拟实现operator[]运算符重载函数

插入元素接口功能:插入元素前,会通过该元素的关键码key检查该元素是否已在哈希表中(不允许冗余):

  • 如果在,返回:pair<指向该元素的迭代器,false>
  • 如果不在,先插入结点,再返回:pair<指向该元素的迭代器,true>
pair < iterator, bool > Insert(const T& data)
		{
			KeyOfT kot;
			iterator it = Find(kot(data));
			if (it!=end())
			{
				return make_pair(it,false);
			}

			Hash hash;

			//负载因子==1时扩容
			if (_n == _tables.size())
			{
				/*size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				HashTable newht;
				newht.resize(newsize);
				for (auto cur : _tables)
				{
					while (cur)
					{
						newht.Insert(cur->_kv);
						cur = cur->_next;
					}
				}
				_tables.swap(newht._tables);*/

				size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				vector<Node*> newtables(newsize, nullptr);
				//for(Node*& cur:_tables)
				for (auto& cur : _tables)
				{
				/*for (size_t i=0;i<_tables.size();++i)
				{
					Node*& cur = _tables[i];*/
					while (cur)
					{
						Node* next = cur->_next;

						size_t hashi = hash(kot(cur->_data)) % newtables.size();

						//头插到新表
						cur->_next = newtables[hashi];
						newtables[hashi] = cur;

						cur = next;

					}
				}
				_tables.swap(newtables);

			}

			size_t hashi = hash(kot(data)) % _tables.size();
			//头插
			Node* newnode = new Node(data);
			newnode->_next = _tables[hashi];
			_tables[hashi] = newnode;

			++_n;
			return make_pair(iterator(newnode,this),false);
		}

4.3 删除结点

   删除结点接口中,需要获取不同类型元素关键码key,因为元素间的比较是通过key值来比较的,以及通过元素关键码key取模计算出哈希位置。
所以需要借助两个仿函数类:

  • 仿函数类KeyOfT,获取不同类型的data中的关键码key值(如果data是key就取key,如果是pair就取first)
  • 仿函数类Hash,有些元素关键码key的类型不能直接取模,需要将其转换成可以取模的size_t类型
bool Erase(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->_data) ==key)
				{
					if (prev == nullptr)
					{
						_tables[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}
					delete cur;
					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}

五、针对除留余数法的取模问题

   实现哈希表,计算元素关键码对应哈希位置的哈希函数采用的是除留余数法,需要传仿函数将不能取模的类型转换成可以取模的size_t类型。
这里给出针对普通类型取模和string类型取模的仿函数,放到全局域中,方便模拟unordered_set和unordered_map时使用。

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

	//特化
	template<>
	struct HashFunc<string>
	{
		//BKDR
		size_t operator()(const string & s)
		{
			//return s[0];

			size_t hash = 0;
			for (auto ch : s)
			{
				hash += ch;
				hash *= 31;
			}
			return hash;
		}
	};

六、unordered_set的模拟实现

   实现unordered_set的各个接口时,就只需要调用底层哈希表对应的接口就行了

#pragma once
#include "HashTable.h"

namespace set
{
	template<class K>
	class unordered_set
	{
	public:
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename HashBucket::HashTable<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename HashBucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;

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

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

		const_iterator begin()  const
		{
			return _ht.begin();
		}

		const_iterator end() const
		{
			return _ht.end();
		}

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

		iterator find(const K& key)
		{
			return _ht.Find(key);
		}

		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}

	private:
		HashBucket::HashTable<K, K, SetKeyOfT> _ht;
	};

	void print(const unordered_set<int>& s)
	{
		unordered_set<int>::const_iterator it = s.begin();
		while (it != s.end())
		{
			//*it = 1;
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}

	void test_unordered_set()
	{
		int a[] = { 3,33,2,13,5,12,1002 };
		unordered_set<int> s;
		for (auto e : a)
		{
			s.insert(e);
		}

		s.insert(103);

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

		for (auto e : s)
		{
			cout << e << " ";
		}
		cout << endl;

		print(s);
	}
}

七、unordered_map的模拟实现

   实现unordered_map的各个接口时,也是调用底层哈希表对应的接口就行了,此外还需要实现[ ]运算符的重载

#pragma once
#include "HashTable.h"

namespace map
{
	template<class K,class V>
	class unordered_map
	{
	public:
		struct MapKeyOfT
		{
			const K& operator()(const pair<K,V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename HashBucket::HashTable<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename HashBucket::HashTable<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;

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

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

		const_iterator begin() const
		{
			return _ht.begin();
		}

		const_iterator end() const
		{
			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 = insert(make_pair(key, V()));
			return ret.first->second;
		}

		iterator find(const K& key)
		{
			return _ht.Find(key);
		}

		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}

	private:
		HashBucket::HashTable<K, pair<const K,V>, MapKeyOfT> _ht;
	};

	void test_unordered_map1()
	{
		unordered_map<int,int> m;
		m.insert(make_pair(1,1));
		m.insert(make_pair(3, 3));
		m.insert(make_pair(2, 2));

		unordered_map<int, int>::iterator it = m.begin();
		while (it != m.end())
		{
			/*it->first = 1;
			it->second = 1;*/

			cout << it->first << ":" << it->second << endl;
			++it;
		}
		cout << endl;
	}

	void test_unordered_map2()
	{
		string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
		"苹果", "香蕉", "苹果", "香蕉" };
		unordered_map<string, int> countMap;
	
		for (auto& e : arr)
		{
			countMap[e]++;
		}

		for (auto& kv : countMap)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
}

八、完整代码

8.1 HashTable.h

#pragma once
#include 

namespace OpenAddress
{

	enum State
	{
		EMPTY,
		EXIST,
		DELETE
	};


	template <class K, class V>
	struct HashData
	{
		pair<K, V> _kv;
		State _state = EMPTY;

	};


	template<class K, class V>
	class HashTable
	{
	public:
		bool Insert(const pair<K, V>& kv)
		{
			if (Find(kv.first))
				return false;

			//负载因子超过0.7就扩容
			//if((double)_n/(double)_tables.size()>=0.7)
			//if (_tables.size()==0 || _n * 10 / _tables.size() >= 7)
			//{
			//	1、表为空,扩不上去
			//	2、光扩容无法访问,size没变
			//	//_tables.reserve(_tables.capacity() * 2); //不能这么做

			//	size_t newsize = _tables.size() == 0 ? 10 : _tables.size()*2;
			//	//_tables.resize(newsize);  //有问题
			//	vector newtables(newsize);
			//	//遍历旧表,重新映射到新表
			//	for (auto& data : _tables)
			//	{
			//		if (data._state==EXIST)
			//		{
			//			//重新算在新表的位置
			//			size_t i = 1;
			//			size_t index = hashi;
			//			while (newtables[hashi]._state == EXIST)
			//			{
			//				index = hashi + i;
			//				index %= newtables.size();
			//				++i;
			//			}

			//			newtables[index]._kv = data._kv;
			//			newtables[index]._state = EXIST;
			//		}
			//	}

			//	_tables.swap(newtables);

			if (_tables.size() == 0 || _n * 10 / _tables.size() >= 7)
			{
				size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				HashTable<K, V> newht;
				newht._tables.resize(newsize);

				//遍历旧表,重新映射到新表
				for (auto& data : _tables)
				{
					if (data._state == EXIST)
					{
						newht.Insert(data._kv);
					}
				}
				_tables.swap(newht._tables);
			}

			size_t hashi = kv.first % _tables.size();

			//线性探测
			size_t i = 1;
			size_t index = hashi;
			while (_tables[index]._state == EXIST)
			{
				index = hashi + i;
				index %= _tables.size();
				++i;
			}

			_tables[index]._kv = kv;
			_tables[index]._state = EXIST;
			_n++;

			return true;
		}


		HashData<K, V>* Find(const K& key)
		{
			if (_tables.size() == 0)
			{
				return nullptr;
			}

			size_t hashi = key % _tables.size();

			//线性探测
			size_t i = 1;
			size_t index = hashi;
			while (_tables[index]._state != EMPTY)
			{
				if (_tables[index]._state == EXIST &&
					_tables[index]._kv.first == key)
				{
					return &_tables[index];
				}

				index = hashi + i;
				index %= _tables.size();
				++i;

				//如果已经查找一圈,那么说明全是存在+删除
				if (index == hashi)
				{
					break;
				}
			}
			return nullptr;
		}


		bool Erase(const K& key)
		{
			HashData<K, V>* ret = Find(key);
			if (ret)
			{
				ret->_state = DELETE;
				--_n;
				return true;
			}
			else
			{
				return false;
			}
		}

	private:
		vector<HashData<K, V>> _tables;
		size_t _n = 0;     //存储的数据个数

		/*HashData* tables;
		size_t size;
		size_t _capacity;*/
	};



	void TestHashTable1()
	{
		int a[] = { 3,33,2,13,5,12,1002 };
		HashTable<int, int> ht;
		for (auto e : a)
		{
			ht.Insert(make_pair(e, e));
		}
		ht.Insert(make_pair(15, 15));

		if (ht.Find(13))
		{
			cout << "13在" << endl;
		}
		else
		{
			cout << "13不在" << endl;
		}

		ht.Erase(13);

		if (ht.Find(13))
		{
			cout << "13在" << endl;
		}
		else
		{
			cout << "13不在" << endl;
		}

	}
}





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

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


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

	//特化
	template<>
	struct HashFunc<string>
	{
		//BKDR
		size_t operator()(const string & s)
		{
			//return s[0];

			size_t hash = 0;
			for (auto ch : s)
			{
				hash += ch;
				hash *= 31;
			}
			return hash;
		}
	};

	//前置声明
	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 __HashIterator
	{
		typedef HashNode<T> Node;
		/*template>
		class HashTable*/
		typedef HashTable<K, T, KeyOfT, Hash> HT;
		typedef __HashIterator < K, T,Ref,Ptr,KeyOfT, Hash> Self;
		typedef __HashIterator < K, T,T&,T*,KeyOfT, Hash> Iterator;

		Node* _node;
		const HT* _ht;

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

		__HashIterator(const Iterator&  it)
			:_node(it._node)
			, _ht(it._ht)
		{}

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

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

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


		//it
		Self& operator++()
		{
			if (_node->_next != nullptr)
			{
				_node = _node->_next;
			}
			else
			{
				//找下一个不为空的桶
				KeyOfT kot;
				Hash hash;
				//算出我当前的桶位置
				size_t hashi = hash(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=HashFunc<K>>
	class HashTable
	{
		template<class K,class T,class Ref,class Ptr,class KeyOfT,class Hash>
		friend struct __HashIterator;

		typedef HashNode<T> Node;
	public:
		typedef __HashIterator<K, T, T&,T*,KeyOfT, Hash> iterator;
		typedef __HashIterator<K, T, const T&,const T*,KeyOfT, Hash> const_iterator;

		iterator begin()
		{
			Node* cur = nullptr;
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				cur = _tables[i];
				if (cur)
				{
					break;
				}
			}

			return iterator(cur, this);
		}

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


		const_iterator begin() const
		{
			Node* cur = nullptr;
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				cur = _tables[i];
				if (cur)
				{
					break;
				}
			}

			return const_iterator(cur, this);
		}

		const_iterator end() const
		{
			return const_iterator(nullptr, this);
		}



		~HashTable()
		{
			for (auto& cur : _tables)
			{
				while (cur)
				{
					Node* next = cur->_next;
					delete cur;
					cur = next;
				}
				cur = nullptr;
			}
		}

		iterator Find(const K& key)
		{
			if (_tables.size() == 0)
				return end();

			KeyOfT kot;
			Hash hash;
			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 Erase(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->_data) ==key)
				{
					if (prev == nullptr)
					{
						_tables[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}
					delete cur;
					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}

		pair < iterator, bool > Insert(const T& data)
		{
			KeyOfT kot;
			iterator it = Find(kot(data));
			if (it!=end())
			{
				return make_pair(it,false);
			}

			Hash hash;

			//负载因子==1时扩容
			if (_n == _tables.size())
			{
				/*size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				HashTable newht;
				newht.resize(newsize);
				for (auto cur : _tables)
				{
					while (cur)
					{
						newht.Insert(cur->_kv);
						cur = cur->_next;
					}
				}
				_tables.swap(newht._tables);*/

				size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				vector<Node*> newtables(newsize, nullptr);
				//for(Node*& cur:_tables)
				for (auto& cur : _tables)
				{
				/*for (size_t i=0;i<_tables.size();++i)
				{
					Node*& cur = _tables[i];*/
					while (cur)
					{
						Node* next = cur->_next;

						size_t hashi = hash(kot(cur->_data)) % newtables.size();

						//头插到新表
						cur->_next = newtables[hashi];
						newtables[hashi] = cur;

						cur = next;

					}
				}
				_tables.swap(newtables);

			}

			size_t hashi = hash(kot(data)) % _tables.size();
			//头插
			Node* newnode = new Node(data);
			newnode->_next = _tables[hashi];
			_tables[hashi] = newnode;

			++_n;
			return make_pair(iterator(newnode,this),false);
		}

		size_t MaxBucketSize()
		{
			size_t max = 0;
			for (size_t i=0;i<_tables.size();++i)
			{
				auto cur = _tables[i];
				size_t size = 0;
				while (cur)
				{
					++size;
					cur = cur->_next;
				}

				printf("[%d]->%d\n", i,size);
				if (size > max)
				{
					max = size;
				}
			}
			return max;
		}

	private:
		vector<Node*> _tables;   //指针数组
		size_t _n=0;    //存储有效数据个数
	};


	//void TestHashTable1()
	//{
	//	int a[] = { 3,33,2,13,5,12,1002 };
	//	HashTable ht;
	//	for (auto e : a)
	//	{
	//		ht.Insert(make_pair(e, e));
	//	}
	//	ht.Insert(make_pair(15, 15));
	//	ht.Insert(make_pair(25, 25));
	//	ht.Insert(make_pair(35, 35));
	//	ht.Insert(make_pair(45, 45));
	//}


	//void TestHashTable2()
	//{
	//	int a[] = { 3,33,2,13,5,12,1002 };
	//	HashTable ht;
	//	for (auto e : a)
	//	{
	//		ht.Insert(make_pair(e, e));
	//	}

	//	ht.Erase(12);
	//	ht.Erase(3);
	//	ht.Erase(33);
	//}

	//struct HashStr
	//{
	//	//BKDR
	//	size_t operator()(const string& s)
	//	{
	//		//return s[0];

	//		size_t hash = 0;
	//		for (auto ch : s)
	//		{
	//			hash += ch;
	//			hash *= 31;
	//		}
	//		return hash;
	//	}
	//};

	//void TestHashTable3()
	//{
	//	//HashTable ht;
	//	HashTable ht;
	//	ht.Insert(make_pair("sort", "排序"));
	//	ht.Insert(make_pair("string", "字符串"));
	//	ht.Insert(make_pair("left", "左边"));
	//	ht.Insert(make_pair("right", "右边"));
	//	ht.Insert(make_pair("", "右边"));

	//	HashStr hashstr;
	//	cout << hashstr("abcd") << endl;
	//	cout << hashstr("bcda") << endl;
	//	cout << hashstr("aadd") << endl;
	//	cout << hashstr("eat") << endl;
	//	cout << hashstr("ate") << endl;
	//}


	//void TestHashTable4()
	//{
	//	size_t N = 10000;
	//	HashTable ht;
	//	srand(time(0));
	//	for (size_t i = 0; i < N; ++i)
	//	{
	//		size_t x = rand();
	//		ht.Insert(make_pair(x, x));
	//	}
	//	cout << ht.MaxBucketSize() << endl;
	//}

	增删改查的时间复杂度:O(1)
	1.负载因子控制
	1.单个桶超过一定长度,这个桶改成挂红黑树

}

8.2 UnorderedSet.h

#pragma once
#include "HashTable.h"

namespace set
{
	template<class K>
	class unordered_set
	{
	public:
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename HashBucket::HashTable<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename HashBucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;

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

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

		const_iterator begin()  const
		{
			return _ht.begin();
		}

		const_iterator end() const
		{
			return _ht.end();
		}

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

		iterator find(const K& key)
		{
			return _ht.Find(key);
		}

		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}

	private:
		HashBucket::HashTable<K, K, SetKeyOfT> _ht;
	};

	void print(const unordered_set<int>& s)
	{
		unordered_set<int>::const_iterator it = s.begin();
		while (it != s.end())
		{
			//*it = 1;
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}

	void test_unordered_set()
	{
		int a[] = { 3,33,2,13,5,12,1002 };
		unordered_set<int> s;
		for (auto e : a)
		{
			s.insert(e);
		}

		s.insert(103);

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

		for (auto e : s)
		{
			cout << e << " ";
		}
		cout << endl;

		print(s);
	}	
}

8.3 UnorderedMap.h

#pragma once
#include "HashTable.h"


namespace map
{
	template<class K,class V>
	class unordered_map
	{
	public:
		struct MapKeyOfT
		{
			const K& operator()(const pair<K,V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename HashBucket::HashTable<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename HashBucket::HashTable<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;

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

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

		const_iterator begin() const
		{
			return _ht.begin();
		}

		const_iterator end() const
		{
			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 = insert(make_pair(key, V()));
			return ret.first->second;
		}

		iterator find(const K& key)
		{
			return _ht.Find(key);
		}

		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}

	private:
		HashBucket::HashTable<K, pair<const K,V>, MapKeyOfT> _ht;
	};

	void test_unordered_map1()
	{
		unordered_map<int,int> m;
		m.insert(make_pair(1,1));
		m.insert(make_pair(3, 3));
		m.insert(make_pair(2, 2));

		unordered_map<int, int>::iterator it = m.begin();
		while (it != m.end())
		{
			/*it->first = 1;
			it->second = 1;*/

			cout << it->first << ":" << it->second << endl;
			++it;
		}
		cout << endl;
	}

	void test_unordered_map2()
	{
		string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
		"苹果", "香蕉", "苹果", "香蕉" };
		unordered_map<string, int> countMap;
	
		for (auto& e : arr)
		{
			countMap[e]++;
		}

		for (auto& kv : countMap)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
}

8.4 test.cpp

#define _CRT_SECURE_NO_WARNINGS 1

#include 
#include
#include
#include
using namespace std;

#include "UnorderedMap.h"
#include "UnorderedSet.h"


int main()
{
	//set::test_unordered_set();
	map::test_unordered_map1();

	return 0;
}

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