用哈希表封装unordered_set和unordered_map

用哈希表封装unordered_set和unordered_map

  • [1. 实现哈希表](#1. 实现哈希表)
  • [2. 封装unordered_map和unordered_set框架,解决KeyOfT](#2. 封装unordered_map和unordered_set框架,解决KeyOfT)
    • [2.1 封装unordered_map和unordered_set框架](#2.1 封装unordered_map和unordered_set框架)
    • [2.2 解决KeyOfT](#2.2 解决KeyOfT)
  • [3. iterator](#3. iterator)
    • [3.1 哈希表的迭代器的框架](#3.1 哈希表的迭代器的框架)
    • [3.2 解决前置++、begin()、end()](#3.2 解决前置++、begin()、end())
    • [3.3 测试begin()、end()、前置++](#3.3 测试begin()、end()、前置++)
  • [4. const_iterator](#4. const_iterator)
  • [5. key不支持修改的问题](#5. key不支持修改的问题)
  • [6. operator[]](#6. operator[])
  • [7. 所有的实现代码](#7. 所有的实现代码)

1. 实现哈希表

红黑树的实现已经在unordered_map和unordered_set的使用以及哈希表的实现详细讲解,这里不再说明。

2. 封装unordered_map和unordered_set框架,解决KeyOfT

2.1 封装unordered_map和unordered_set框架

  1. 因为哈希表底层不知道你传过来的到底是K,还是pair<K, V>,所以哈希表的节点把类型统一改为泛型T代替。我会标记一下修改的代码。
  2. 哈希表的两个模板参数的作用:第一个模板参数K:它是为了find只传一个key值做准备;第二个模板参数T:它就是节点数据类型。
  3. 可以发现如果传泛型T,在进行取模的时候是不知道怎么取模的,对于unordered_set直接取模就行了,因为它传的是K,对于unordered_map就要用pair的first去取模了,所以下一步就是要解决KeyOfT问题,就是要取出T中的Key。
cpp 复制代码
// HashTable.h
namespace hash_bucket
{
	template<class T> // 修改
	struct HashNode
	{
		T _data; // 修改
		HashNode<T>* _next; // 修改

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

	template<class K, class T, class Hash = HashFunc<K>> // 修改
	class HashTable
	{
		typedef HashNode<T> Node; // 修改
	public:
		//...

		bool Insert(const T& data) // 修改
		{
			//...
			
			Node* newnode = new Node(data); // 修改
			// 头插
			newnode->_next = _tables[hashi];
			_tables[hashi] = newnode; 
			++_n;
		
			return true;
		}

下面是unordered_map和unordered_set的准备工作:

unordered_map和unordered_set的成员变量的类型是一个哈希表,unordered_map和unordered_set属于上层,unordered_map知道第二个参数传pair<K, V>,unordered_set知道第二个参数传K。

cpp 复制代码
//unordered_set.h
#pragma once

#include "HashTable.h"

namespace bs
{
	template<class K>
	class unordered_set
	{
	private:
		hash_bucket::HashTable<K, K> _ht;
	};
}

// unordered_map.h
#pragma once

#include "HashTable.h"

namespace bs
{
	template<class K, class V>
	class unordered_map
	{
	private:
		hash_bucket::HashTable<K, pair<K, V>> _ht;
	};
}

2.2 解决KeyOfT

为了解决key值取模的问题,我们新增了一个模板参数KeyOfT;它存在的意义就是从T中取出key值,进行取模。

cpp 复制代码
//HashTable.h
template<class K, class T, class KeyOfT, class Hash = HashFunc<K>> // 修改
class HashTable
{
	typedef HashNode<T> Node;
public:
	//...

	bool Insert(const T& data)
	{
		KeyOfT kot; // 修改

		if (Find(kot(data))) // 修改
		{
			return false;
		}

		Hash hs;

		// 负载因子到 1 就扩容
		if (_n == _tables.size())
		{
			vector<Node*> newtables(__stl_next_prime(_tables.size() + 1), nullptr);
			// 遍历旧表,将旧表的节点全部拿下来,挂到新表上
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				Node* cur = _tables[i];
				while (cur)
				{
					Node* next = cur->_next;
					// cur头插到新表
					size_t hashi = hs(kot(cur->_data)) % newtables.size(); // 修改
					cur->_next = newtables[hashi];
					newtables[hashi] = cur;

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

		size_t hashi = hs(kot(data)) % _tables.size(); // 修改

		Node* newnode = new Node(data);
		// 头插
		newnode->_next = _tables[hashi];
		_tables[hashi] = newnode; 
		++_n;

		return true;
	}

	Node* Find(const K& key)
	{
		KeyOfT kot; // 修改
		Hash hs;
		size_t hashi = hs(key) % _tables.size();
		Node* cur = _tables[hashi];
		while (cur)
		{
			if (kot(cur->_data) == key) // 修改
				return cur;

			cur = cur->_next;
		}

		return nullptr;
	}

	bool Erase(const K& key)
	{
		KeyOfT kot; // 修改
		Hash hs;
		size_t hashi = hs(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;
				}
				--_n;
				delete cur;
				return true;
			}

			prev = cur;
			cur = cur->_next;
		}

		return false;
	}
	//...

既然哈希表新增了一个模板参数,那么我们的unordered_map和unordered_set的_ht的类型也要跟着改,我们这里用到了仿函数,它可以当参数类型传过去,我们只需要写一个返回key值的仿函数就可以解决KeyOfT问题。

cpp 复制代码
// unordered_set.h
#pragma once

#include "HashTable.h"

namespace bs
{
	template<class K>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

	public:
		bool insert(const K& key)
		{
			return _ht.Insert(key);
		}

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

// unordered_map.h
#pragma once

#include "HashTable.h"

namespace bs
{
	template<class K, class V>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};

	public:
		bool insert(const pair<K, V>& kv)
		{
			return _ht.Insert(kv);
		}
	private:
		hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> _ht;
	};
}

3. iterator

3.1 哈希表的迭代器的框架

模板参数加KeyOfT和Hash并且成员变量多定义了一个哈希表的指针是要在前置++中用。

cpp 复制代码
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;
	HT* _pht;

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

	Self& operator++()
	{
		//...
	}

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

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

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

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

};

3.2 解决前置++、begin()、end()

前置++的实现是一个难点,iterator中有⼀个指向结点的指针,如果当前桶下⾯还有结点,则结点的指针指向下⼀个结点即可。如果当前桶⾛完了,则需要用哈希表对象的指针计算找到下⼀个桶,⽤key值计算出当前桶位置,依次往后找下⼀个不为空的桶即可。

cpp 复制代码
Self& operator++()
{
	if (_node->_next)
	{
		// 当前桶还没有走完
		_node = _node->_next;
	}
	else
	{
		// 当前桶走完了,需要找下一个不为空的桶里面的第一个节点
		KeyOfT kot;
		Hash hs;
		size_t hashi = hs(kot(_node->_data)) % _pht->_tables.size();
		++hashi;
		while (hashi < _pht->_tables.size())
		{
			if (_pht->_tables[hashi])
			{
				_node = _pht->_tables[hashi];
				break;
			}
			++hashi;
		}
	}

	if (hashi == _pht->_tables.size())
	{
		// 所有桶都走完了,置为end()
		_node = nullptr;
	}

	return *this;
}

begin()返回有数据的第⼀个桶中第⼀个节点指针构造的迭代器,这⾥end()返回迭代器可以⽤空表示。

cpp 复制代码
template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>
class HashTable
{
	typedef HashNode<T> Node;
public:
	typedef HTIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;

	Iterator Begin()
	{
		if (_n == 0)
		{
			return End();
		}

		for (size_t i = 0; i < _tables.size(); ++i)
		{
			if (_tables[i])
			{
				return Iterator(_tables[i], this);
			}
		}

		// 运行逻辑上不会走到这里,但语法逻辑上要给返回值
		return End();
	}

	Iterator End()
	{
		return Iterator(nullptr, this);
	}
	//...

3.3 测试begin()、end()、前置++

终于写出来了,你开心极了,然后运行一下,发现报了一堆错误!

为什么啊!!!我感觉没一点问题啊?这里相较于用红黑树封装map和set的区别就在于迭代器和哈希表都用了对方设置了一个对方的成员变量,对于哈希表还好,迭代器在前面编译器能扫描到,但是对于迭代器就不行了,所以要在迭代器前加一个哈希表的前置声明。

cpp 复制代码
// 前置声明
template<class K, class T, class KeyOfT, class Hash> // 注意声明时Hash不能带缺省值
class HashTable;

	template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
struct HTIterator
{
//...

先把哈希表的迭代器、begin()和end()拿过来,然后套用一下,再测试。

cpp 复制代码
// unordered_set.h
#pragma once

#include "HashTable.h"

namespace bs
{
	template<class K>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

	public:
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::Iterator iterator;

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

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

		bool insert(const K& key)
		{
			return _ht.Insert(key);
		}

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

	void test_unordered_set()
	{
		unordered_set<int> s;
		s.insert(3);
		s.insert(4);
		s.insert(2);
		s.insert(8);
		s.insert(4);
		s.insert(11);
		s.insert(23);
		s.insert(5);
		s.insert(2);
		unordered_set<int>::iterator it = s.begin();
		while (it != s.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}
}

// myset.h
#pragma once

#include "HashTable.h"

namespace bs
{
	template<class K, class V>
	class unordered_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>::Iterator iterator;

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

		iterator end()
		{
			return _ht.End();
		}
		bool insert(const pair<K, V>& kv)
		{
			return _ht.Insert(kv);
		}
	private:
		hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> _ht;
	};

	void test_unordered_map()
	{
		unordered_map<string, string> m;
		m.insert({ "sort", "排序" });
		m.insert({ "left", "左边" });
		m.insert({ "right", "右边" });

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

怎么又这么多报错,我要红温了!!!一看发现是迭代器用哈希表的私有成员_tables了,那怎么办,难道要把_tables改成共有吗?其实只需要让迭代器成为哈希表的友元类就行了,这样迭代器就能用哈希表的私有了。

cpp 复制代码
	template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>
	class HashTable
	{
		// 迭代器要访问哈希表的私有成员_tables,就把迭代器变为哈希表的友元
		//友元声明
		template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
		friend struct HTIterator;

		typedef HashNode<T> Node;
	public:
		typedef HTIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;
		//...

欧耶!终于成功了,好有成就感啊。

4. const_iterator

const_iterator就是复制粘贴iterator的代码,再稍加修改即可。

顺便测试一下。

cpp 复制代码
//HashTable.h
template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>
class HashTable
{
	// 迭代器要访问哈希表的私有成员_tables,就把迭代器变为哈希表的友元
	//友元声明
	template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
	friend struct HTIterator;

	typedef HashNode<T> Node;
public:
	typedef HTIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;
	typedef HTIterator<K, T, const T&, const T*, KeyOfT, Hash> ConstIterator;

	Iterator Begin()
	{
		if (_n == 0)
		{
			return End();
		}

		for (size_t i = 0; i < _tables.size(); ++i)
		{
			if (_tables[i])
			{
				return Iterator(_tables[i], this);
			}
		}

		// 运行逻辑上不会走到这里,但语法逻辑上要给返回值
		return End();
	}

	Iterator End()
	{
		return Iterator(nullptr, this);
	}

	ConstIterator Begin() const
	{
		if (_n == 0)
		{
			return End();
		}

		for (size_t i = 0; i < _tables.size(); ++i)
		{
			if (_tables[i])
			{
				return ConstIterator(_tables[i], this);
			}
		}

		// 运行逻辑上不会走到这里,但语法逻辑上要给返回值
		return End();
	}

	ConstIterator End() const
	{
		return ConstIterator(nullptr, this);
	}
	//...

//unordered_set.h
#pragma once

#include "HashTable.h"

namespace bs
{
	template<class K>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

	public:
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::Iterator iterator;
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::ConstIterator 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();
		}

		bool insert(const K& key)
		{
			return _ht.Insert(key);
		}

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

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

	void test_unordered_set()
	{
		unordered_set<int> s;
		s.insert(3);
		s.insert(4);
		s.insert(2);
		s.insert(8);
		s.insert(4);
		s.insert(11);
		s.insert(23);
		s.insert(5);
		s.insert(2);
		unordered_set<int>::iterator it = s.begin();
		while (it != s.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

		Print(s);
	}
}

// unordered_map.h
#pragma once

#include "HashTable.h"

namespace bs
{
	template<class K, class V>
	class unordered_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>::Iterator iterator;
		typedef typename hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT>::ConstIterator 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();
		}

		bool insert(const pair<K, V>& kv)
		{
			return _ht.Insert(kv);
		}
	private:
		hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> _ht;
	};

	void Print(const unordered_map<string, string>& m)
	{
		for (auto& e : m)
		{
			cout << e.first << ":" << e.second << endl;
		}
	}

	void test_unordered_map()
	{
		unordered_map<string, string> m;
		m.insert({ "sort", "排序" });
		m.insert({ "left", "左边" });
		m.insert({ "right", "右边" });

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

		Print(m);
	}
}

怎么又报错到const迭代器了,我真的崩溃了!!!

这里报错的原因是:End()返回的是const的哈希表的指针,但是迭代器的成员变量中的哈希表的指针没加const,属于是权限放大了,因为在迭代器中我们不会改变哈希表的指针,所以直接在迭代器那里加上const就行了。

cpp 复制代码
Node* _node;
const HT* _pht; // 加 const

HTIterator(Node* node, const HT* pht) // 加 const
	:_node(node)
	,_pht(pht)
{}

欧耶,我又成功了,难道我真的是C++真神吗!!!

5. key不支持修改的问题

当前,我们的unordered_map的key和value以及unordered_set的key都是可以修改的,所以要解决key不支持修改的问题。

想解决该问题只需要把unordered_map和unordered_set中,传给哈希表底层的迭代器的key值加上const以及成员变量_ht的第二个参数的类型的key值加上const即可。

cpp 复制代码
//unordered_set.h
typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT>::Iterator iterator;
typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT>::ConstIterator const_iterator;

hash_bucket::HashTable<K, const K, SetKeyOfT> _ht;

//unordered_map.h
typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;
typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::ConstIterator const_iterator;

// pair可以修改,pair的K用const修饰,保证key不能修改,value可以修改
hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT> _ht;



6. operator[]

在解决operator[]之前,要先解决insert和find的返回值类型问题;insert要返回pair<iterator, bool>,fnd要返回iterator。

cpp 复制代码
// HashTable.h
pair<Iterator, bool> Insert(const T& data) // 修改
{
	KeyOfT kot;
	Iterator it = Find(kot(data)); // 修改
	if (it != End()) // 修改
	{
		return { it, false }; // 修改
	}
	//...
	
	return { Iterator(newnode, this), true }; // 修改
}

Iterator Find(const K& key) // 修改
{
	KeyOfT kot;
	Hash hs;
	size_t hashi = hs(key) % _tables.size();
	Node* cur = _tables[hashi];
	while (cur)
	{
		if (kot(cur->_data) == key)
			return Iterator(cur, this); // 修改

		cur = cur->_next;
	}

	return Iterator(nullptr, this); // 修改
}

// unordered_set.h
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);
}

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

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

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

unordered_map的operator[]

cpp 复制代码
V& operator[](const K& key)
{
	pair<iterator, bool> ret = insert({ key, V() });
	iterator it = ret.first;
	return it->second;
}

测试代码

cpp 复制代码
void Print(const unordered_map<string, string>& m)
{
	for (auto& e : m)
	{
		cout << e.first << ":" << e.second << endl;
	}
}

void test_unordered_map()
{
	unordered_map<string, string> m;
	m.insert({ "sort", "排序" });
	m.insert({ "left", "左边" });
	m.insert({ "right", "右边" });
	m["insert"]; // 插入
	Print(m);
	cout << endl;
	m["insert"] = "插入"; // 修改
	Print(m);
	cout << endl;
	m["string"] = "字符串"; // 插入+修改
	Print(m);
	cout << endl;

	for (auto& e : m)
	{
		//e.first += 'x'; // key不支持修改
		//e.second += 'y'; 
		cout << e.first << ":" << e.second << endl;
	}

	Print(m);

	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
	unordered_map<string, int> countMap;

	for (auto& str : arr)
	{
		// 1、不在,说明水果第一次出现,则插入{水果, 0},同时返回次数的引用,++就变成1次了
		// 2、在,则返回水果对应的次数的引用并++
		countMap[str]++;
	}

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

这里还有一个细节,就是哈希表底层的模板参数Hash的缺省值,按理说这里不该有缺省值,因为哈希表是底层,这个应该要上层的unordered_set和unordered_map传过来才对,其实很简单,就是在unordered_set和unordered_map的模板参数上加一个Hash并带上缺省值,然后传给哈希表的迭代器和HashTable,最后再把HashTable的缺省值去掉就行了

7. 所有的实现代码

unordered_set.h

cpp 复制代码
#pragma once

#include "HashTable.h"

namespace bs
{
	template<class K, class Hash = HashFunc<K>>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

	public:
		typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::Iterator iterator;
		typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::ConstIterator 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:
		hash_bucket::HashTable<K, const K, SetKeyOfT, Hash> _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()
	{
		unordered_set<int> s;
		s.insert(3);
		s.insert(4);
		s.insert(2);
		s.insert(8);
		s.insert(4);
		s.insert(11);
		s.insert(23);
		s.insert(5);
		s.insert(2);
		unordered_set<int>::iterator it = s.begin();
		while (it != s.end())
		{
			//*it += 1; // key不支持修改
			cout << *it << " ";
			++it;
		}
		cout << endl;

		Print(s);
	}
}

unordered_map.h

cpp 复制代码
#pragma once

#include "HashTable.h"

namespace bs
{
	template<class K, class V, class Hash = HashFunc<K>>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};

	public:
		typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::Iterator iterator;
		typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::ConstIterator 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);
		}

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

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

		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert({ key, V() });
			iterator it = ret.first;
			return it->second;
		}

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

	void Print(const unordered_map<string, string>& m)
	{
		for (auto& e : m)
		{
			cout << e.first << ":" << e.second << endl;
		}
	}

	void test_unordered_map()
	{
		unordered_map<string, string> m;
		m.insert({ "sort", "排序" });
		m.insert({ "left", "左边" });
		m.insert({ "right", "右边" });
		m["insert"]; // 插入
		Print(m);
		cout << endl;
		m["insert"] = "插入"; // 修改
		Print(m);
		cout << endl;
		m["string"] = "字符串"; // 插入+修改
		Print(m);
		cout << endl;

		for (auto& e : m)
		{
			//e.first += 'x'; // key不支持修改
			//e.second += 'y'; 
			cout << e.first << ":" << e.second << endl;
		}

		Print(m);

		string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
		unordered_map<string, int> countMap;

		for (auto& str : arr)
		{
			// 1、不在,说明水果第一次出现,则插入{水果, 0},同时返回次数的引用,++就变成1次了
			// 2、在,则返回水果对应的次数的引用并++
			countMap[str]++;
		}

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

HashTable.h

cpp 复制代码
#pragma once

// 素数表
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;
}

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

// 特化
template<>
struct HashFunc<string>
{
	size_t operator()(const string& key) const
	{
		size_t hash = 0;
		for (auto ch : key)
		{
			hash += ch;
			hash *= 131;
		}
		return hash;
	}
};

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

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

	// 前置声明
	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* _pht;

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

		Self& operator++()
		{
			if (_node->_next)
			{
				// 当前桶还没有走完
				_node = _node->_next;
			}
			else
			{
				// 当前桶走完了,需要找下一个不为空的桶里面的第一个节点
				KeyOfT kot;
				Hash hs;
				size_t hashi = hs(kot(_node->_data)) % _pht->_tables.size();
				++hashi;
				while (hashi < _pht->_tables.size())
				{
					if (_pht->_tables[hashi])
					{
						_node = _pht->_tables[hashi];
						break;
					}
					++hashi;
				}

				if (hashi == _pht->_tables.size())
				{
					// 所有桶都走完了,置为end()
					_node = nullptr;
				}
			}

			return *this;
		}

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

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

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

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

	};

	template<class K, class T, class KeyOfT, class Hash>
	class HashTable
	{
		// 迭代器要访问哈希表的私有成员_tables,就把迭代器变为哈希表的友元
		//友元声明
		template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
		friend struct HTIterator;

		typedef HashNode<T> Node;
	public:
		typedef HTIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;
		typedef HTIterator<K, T, const T&, const T*, KeyOfT, Hash> ConstIterator;

		Iterator Begin()
		{
			if (_n == 0)
			{
				return End();
			}

			for (size_t i = 0; i < _tables.size(); ++i)
			{
				if (_tables[i])
				{
					return Iterator(_tables[i], this);
				}
			}

			// 运行逻辑上不会走到这里,但语法逻辑上要给返回值
			return End();
		}

		Iterator End()
		{
			return Iterator(nullptr, this);
		}

		ConstIterator Begin() const
		{
			if (_n == 0)
			{
				return End();
			}

			for (size_t i = 0; i < _tables.size(); ++i)
			{
				if (_tables[i])
				{
					return ConstIterator(_tables[i], this);
				}
			}

			// 运行逻辑上不会走到这里,但语法逻辑上要给返回值
			return End();
		}

		ConstIterator End() const
		{
			return ConstIterator(nullptr, this);
		}

		HashTable(size_t n = __stl_next_prime(0))
			:_tables(n, nullptr)
			,_n(0)
		{}

		~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<Iterator, bool> Insert(const T& data)
		{
			KeyOfT kot;
			Iterator it = Find(kot(data));
			if (it != End())
			{
				return { it, false };
			}

			Hash hs;

			// 负载因子到 1 就扩容
			if (_n == _tables.size())
			{
				vector<Node*> newtables(__stl_next_prime(_tables.size() + 1), nullptr);
				// 遍历旧表,将旧表的节点全部拿下来,挂到新表上
				for (size_t i = 0; i < _tables.size(); ++i)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;
						// cur头插到新表
						size_t hashi = hs(kot(cur->_data)) % newtables.size();
						cur->_next = newtables[hashi];
						newtables[hashi] = cur;

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

			size_t hashi = hs(kot(data)) % _tables.size();

			Node* newnode = new Node(data);
			// 头插
			newnode->_next = _tables[hashi];
			_tables[hashi] = newnode; 
			++_n;

			return { Iterator(newnode, this), true };
		}

		Iterator Find(const K& key)
		{
			KeyOfT kot;
			Hash hs;
			size_t hashi = hs(key) % _tables.size();
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
					return Iterator(cur, this);

				cur = cur->_next;
			}

			return Iterator(nullptr, this);
		}
	
		bool Erase(const K& key)
		{
			KeyOfT kot;
			Hash hs;
			size_t hashi = hs(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;
					}
					--_n;
					delete cur;
					return true;
				}

				prev = cur;
				cur = cur->_next;
			}

			return false;
		}

	private:
		vector<Node*> _tables;
		size_t _n; // 实际存储有效数据个数
	};
}

test.cpp

cpp 复制代码
#include <iostream>
#include <vector>
#include <string>

using namespace std;

#include "HashTable.h"
#include "unordered_set.h"
#include "unordered_map.h"

int main()
{
	bs::test_unordered_set();

	bs::test_unordered_map();

	return 0;
}
相关推荐
懒羊羊不懒@2 小时前
线性表的实现
c语言·数据结构
waves浪游2 小时前
C++多态
开发语言·c++
aramae3 小时前
快速排序的深入优化探讨
c语言·开发语言·c++·算法·排序算法
Miraitowa_cheems3 小时前
LeetCode算法日记 - Day 62: 黄金矿工、不同路径III
数据结构·算法·leetcode·决策树·职场和发展·深度优先·剪枝
qq_437896434 小时前
unsigned 是等于 unsigned int
开发语言·c++·算法·c
952364 小时前
数据结构—单链表
c语言·数据结构·学习
greentea_20134 小时前
Codeforces Round 863 A. Insert Digit (1811)
数据结构·算法
Yupureki5 小时前
从零开始的C++学习生活 2:类和对象(上)
c语言·开发语言·c++·学习·visual studio
-雷阵雨-6 小时前
数据结构——栈和队列(模拟实现)
java·开发语言·数据结构·intellij-idea