【STL】用一张哈希表封装unordered_set和unordered_map

哈希表源代码

这里是使用开散列实现的哈希表,为了和库里的哈希表进行区分,我将哈希表放入到了命名空间中

cpp 复制代码
//确保取余运算符两边是正整数,下标不能是负整数
template<class K>
struct DefaultHashFunc
{
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};

//模板的特化, 让哈希表可以使用string类型
template<>
struct DefaultHashFunc<string>
{
	size_t operator()(const string& key)
	{
		size_t hash = 0;
		for (auto ch : key)
		{
			//使用上一个ASCII码乘以131,避免得到相同的值,注意并不能完全避免,如果数据量大还是会有相同的数
			hash = hash * 131 + ch;
		}

		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 HashFunc>
	class HashTable;

	//哈希表迭代器(单向迭代器)
	template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc = DefaultHashFunc<K>>
	struct __Hash_iterator
	{
		typedef HashNode<T> Node;
		typedef __Hash_iterator<K, T, Ref, Ptr, KeyOfT, HashFunc> self;
		typedef __Hash_iterator<K, T, T&, T*, KeyOfT> iterator;
		typedef __Hash_iterator<K, T, const T&, const T*, KeyOfT> iterator;

		Node* _node;
		//HashTable<K, T, KeyOfT, HashFunc>* _pht;
		const HashTable<K, T, KeyOfT, HashFunc>* _pht;
		//为了可以使用const迭代器我们使用const类型接收
		__Hash_iterator(const iterator& it)
			:_node(it._node)
			,_pht(it._pht)
		{}

		__Hash_iterator(Node* node, const HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			,_pht(pht)
		{}

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

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

		self& operator++()
		{
			if (_node->next)
			{
				_node = _node->next;
				return *this;
			}
			else
			{
				KeyOfT kot;
				HashFunc kov;

				size_t Hashi = kov(kot(_node->_data)) % _pht->_table.size();
				++Hashi;
				while (Hashi < _pht->_table.size())
				{
					if (_pht->_table[Hashi])
					{
						_node = _pht->_table[Hashi];
						return *this;
					}
					++Hashi;
				}
			}
			_node = nullptr;
			return *this;
		}

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

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

	size_t GetNextPrime(size_t prime)
	{
		static const int PRIMECOUNT = 28;
		static const unsigned long primeList[PRIMECOUNT] =
		{
		  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
		};

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

		return primeList[i];
	}

	template<class K, class T, class KeyOfT, class HashFunc = DefaultHashFunc<K>>
	class HashTable
	{
		typedef HashNode<T> Node;

		//友元声明。类模板也需要写,注意和前置声明一样不能有缺省值
		template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc>
		friend struct __Hash_iterator;

	public:
		typedef __Hash_iterator<K, T, T&, T*, KeyOfT> iterator;
		typedef __Hash_iterator<K, T, const T&, const T*, KeyOfT> const_iterator;

		iterator begin()
		{
			size_t Hashi = 0;
			while (_table[Hashi] == nullptr)
				++Hashi;

			return iterator(_table[Hashi], this);
		}

		const_iterator begin() const
		{
			size_t Hashi = 0;
			while (_table[Hashi])
				++Hashi;

			return const_iterator(_table[Hashi], this);
		}

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

		const_iterator end() const
		//const * const this
		{
			//这里的this指针被加了const,我们手动加上的const是修饰this指向的内容,所以在迭代器类中我们需要使用一个const类型的参数接收,不然会导致类型不一致
			//如果const修饰的this指针本身的话就没事,因为在迭代器类中我们只需要去读它并不需要去写它
			return const_iterator(nullptr, this);
		}

		HashTable()
		{
			_table.resize(10, nullptr);
		}

		HashTable(const HashTable<K, T, KeyOfT, HashFunc>& H)
		{
			HashFunc kov;
			KeyOfT kot;
			size_t n = H._table.size();
			_table.resize(n);

			for (size_t i = 0; i < n; ++i)
			{
				Node* cur = H._table[i];
				while (cur)
				{
					size_t Hashi = kov(kot(cur->_data)) % n;
					Node* newnode = new Node(H._table[Hashi]->_data);
					//头插
					newnode->next = _table[i];
					_table[i] = newnode;

					cur = cur->next;
				}
			}
		}

		~HashTable()
		{
				for (size_t i = 0; i < _table.size(); ++i)
				{
					Node* cur = _table[i];
					while (cur)
					{
						Node* next = cur->next;
						delete cur;
						cur = next;
					}

					_table[i] = nullptr;
				}
		}

		iterator Find(const K& key)
		{
			HashFunc kov;
			KeyOfT kot;

			size_t Hashi = kov(key) % _table.size();

			Node* cur = _table[Hashi];
			while (cur != nullptr)
			{
				Node* next = cur->next;

				if (kot(cur->_data) == key)
					return iterator(cur, this);

				cur = next;
			}

			return end();
		}

		pair<iterator, bool> insert(const T& data)
		{
			HashFunc kov;
			KeyOfT kot;

			if (Find(kot(data))._node)
				return make_pair(Find(kot(data)), false);
			//负载因子处理扩容
			//为1进行扩容
			if (_n == _table.size())
			{
				size_t newSize = GetNextPrime(_table.size());
				vector<Node*> newtable;
				newtable.resize(newSize, nullptr);
				
				//哈希桶上链接的结点位置是任意的,头插,尾插都可以
				for(size_t i = 0; i < _table.size(); ++i)
				{
					Node* cur = _table[i];
					//将cur上哈希桶的每个结点都给链接到_table上
					//将旧哈希桶上的元素重新映射到新哈希表上
					while (cur)
					{
						Node* next = cur->next;
						size_t Hashi = kov(kot(cur->_data)) % newSize;

						//头插操作,将cur理解为一个新结点头插到这个newtable上
						cur->next = newtable[Hashi];
						newtable[Hashi] = cur;

						cur = next;
					}
				}
				_table.swap(newtable);
			}

			size_t hashi = kov(kot(data)) % _table.size();

			//头插
			Node* newnode = new Node(data);
			newnode->next = _table[hashi];
			_table[hashi] = newnode;
			++_n;
			
			return make_pair(iterator(newnode, this), true);
		}

		bool erase(const K& key)
		{
			if (Find(key) == nullptr)
				return false;

			HashFunc kov;
			KeyOfT kot;

			size_t Hashi = kov(key) % _table.size();

			Node* cur = _table[Hashi];
			Node* prev = nullptr;

			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev == nullptr)
					{
						_table[Hashi] = cur->next;
					}
					else
					{
						prev->next = cur->next;
					}
					delete cur;
					return true;
				}

				prev = cur;
				cur = cur->next;
			}

			return false;
		}

	private:
		vector<Node*> _table;
		size_t _n = 0;
	};
}

1.创建unordered_set和unordered_map,unordered_set和unordered_map分别是Key,Key模型和Key,Value模型,使用一张哈希表封装这两个的容器的话,我们需要同时兼容这两种模型

哈希表的结点类

这里为了能根容易的兼容set和map,我们将结点类的模板参数从 K V 改为了T,至于为什么改为一个模板参数,我们来看看后面的设计

cpp 复制代码
template<class T>
struct HashNode
{
	T _data;
	HashNode<T>* next;

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

哈希表类

由于我们改变了模板类的模板参数,为了更好的配合结点类,这里我们将哈希表的第二个模板参数

也改为T

cpp 复制代码
template<class K, class T, class KeyOfT, class HashFunc = DefaultHashFunc<K>>
class HashTable
{
	typedef HashNode<T> Node;
public:
    //...
private:
    vector<Node*> _table;
    size_t _n; //哈希表中有效元素的个数
}

map

哈希表的上层容器如果是map,那么哈希表模板参数接收的类型就是Key和Value组成的键值对

cpp 复制代码
template<class K, class V>
class UnOrderMap
{
	struct MapKeyOfT
	{
		const K& operator()(const pair<K, V>& key)
		{
			return key.first;
		}
	};
public:
    //...
private:
   Hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> _t;
};

set

哈希表的上层容器如果是set,那么哈希表模板参数接收的类型就是Key和Key组成的键值对

cpp 复制代码
template<class K>
class UnOrderSet
{
	struct MapKeyOfT
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
public:
    //...
private:
    Hash_bucket::HashTable<K, K, SetKeyOfT> _t;
};

从上图可以看到,模板参数T的类型是由它的上层容器所决定的,至于这里unordered_set和unordered_map为什么需要单独的传给哈希表仿函数,因为对于底层的哈希表而言它并不知道它将会接收什么样类型由于这个问题,我们干脆直接在它的上层容器中创建一个仿函数,使用unordered_set时就传给它set的仿函数,利用这个仿函数拿到unordered_set的Key值并且使用它,同理unordered_map也是一样

其实对于unordered_set而言它是Key, Key类型,处理不处理都一样,创建仿函数的主要原因是因为unordered_map传入哈希表中T实例化的类型是Key-Value的键值对,在底层哈希表中时我们大部分场景都需要使用它的Key值,由于这个原因,我们给unordered_map创建了仿函数,为了接收unordered_map的仿函数,底层的哈希表不得不增加模板参数来接收,这时如果unordered_set不传也不合适,所以为了将就unordered_map,unordered_set也创建了一个仿函数来配合

哈希表迭代器的创建

这里的思路:先处理好哈希表的迭代器,然后处理unordered_map,unordered_set的迭代器

哈希表迭代器的前置++

创建哈希表迭代器需要用到两个元素,使其向后寻找下一个位置

**1.链表指针:**遍历哈希桶找到它的下一个结点,所以需要它的指针

2.哈希表指针: 当所给指针的下一个位置为空时,我们需要继续向后遍历哈希表,寻找其下一个位置,直到哈希表为空结束,所以哈希表指针的作用就是定位到该桶所在位置的下标往后找下一个位置

**情况一:**所给结点下一个位置不为空

**情况二:**当所给结点下一个位置为空时

情况三: 一直遍历到最后都没有找到,我这里的处理是直接返回nullptr

说明一下迭代器器所需要的模板参数,因为我们需要使用哈希表指针,哈希表的模板参数需要全带过来,然后再加上迭代器所需要的参数,一叠加这里就有六个模板参数

迭代器代码实现

其他的实现都比较简单这里就全部给出来了

cpp 复制代码
//哈希表迭代器(单向迭代器)
template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc = DefaultHashFunc<K>>
struct __Hash_iterator
{
	typedef HashNode<T> Node;
	typedef __Hash_iterator<K, T, Ref, Ptr, KeyOfT, HashFunc> self;

	Node* _node;
	HashTable<K, T, KeyOfT, HashFunc>* _pht;

	__Hash_iterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
		:_node(node)
		,_pht(pht)
	{}

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

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

	self& operator++()
	{
		if (_node->next)
		{
			_node = _node->next;
			return *this;
		}
		else
		{
			KeyOfT kot;
			HashFunc kov;

			size_t Hashi = kov(kot(_node->_data)) % _pht->_table.size();
			++Hashi;
			while (Hashi < _pht->_table.size())
			{
				if (_pht->_table[Hashi])
				{
					_node = _pht->_table[Hashi];
					return *this;
				}
				++Hashi;
			}
		}
		_node = nullptr;
		return *this;
	}

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

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

普通迭代器

begin()

遍历哈希表找到第一个不为空的结点即可

cpp 复制代码
iterator begin()
{
	size_t Hashi = 0;
	while (_table[Hashi] == nullptr)
		++Hashi;

	return iterator(_table[Hashi], this);
}

end()

使用nullptr构造一个end迭代器就可以了

cpp 复制代码
iterator end()
{
	return iterator(nullptr, this);
}

const迭代器

begin()

cpp 复制代码
const_iterator begin() const
{
	size_t Hashi = 0;
	while (_table[Hashi])
		++Hashi;

	return const_iterator(_table[Hashi], this);
}

end()

我们知道再接口右侧增加const代表这里的this指针被加了const,我们手动加上的const是修饰this指向的内容,所以在迭代器类中我们需要使用一个const类型的参数接收,不然会导致类型不一致而报错,(它默认的const修饰的是this指针本身)

cpp 复制代码
const_iterator end() const
//const * const this
{
	return const_iterator(nullptr, this);
}

对迭代器类的修改:

cpp 复制代码
template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc = DefaultHashFunc<K>>
struct __Hash_iterator
{
	typedef HashNode<T> Node;
	typedef __Hash_iterator<K, T, Ref, Ptr, KeyOfT, HashFunc> self;

	Node* _node;
	const HashTable<K, T, KeyOfT, HashFunc>* _pht;

	__Hash_iterator(Node* node, const HashTable<K, T, KeyOfT, HashFunc>* pht)
		:_node(node)
		,_pht(pht)
	{}

    //...
};

我们只需要在哈希表类型前面加上cons,加上const之后我们即可传const类型也可以传普通类型正好符合我们的需求(在迭代器类中我们需要一个哈希表指针,它在该类中的作用只需要可读就行并不需要写,加上const反而更好)

迭代器创建好了之后会引发几个问题,第一个问题 哈希表中使用迭代器,迭代器里有哈希表会造成迭代器和哈希表的互相依赖,在使用迭代器类时需要实例化哈希表类,反之,也是同理

如下图所示:

解决方法: 使用 前置说明 告诉编译器我的某个类定义过

cpp 复制代码
//解决迭代器和哈希表互相依赖问题
//前置声明(告诉编译器我的这个类定义过)
template<class K, class T, class KeyOfT, class HashFunc>
class HashTable;

template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc = DefaultHashFunc<K>>
struct __Hash_iterator
{
	typedef HashNode<T> Node;
	typedef __Hash_iterator<K, T, Ref, Ptr, KeyOfT, HashFunc> self;

	Node* _node;
	const HashTable<K, T, KeyOfT, HashFunc>* _pht;

	__Hash_iterator(Node* node, const HashTable<K, T, KeyOfT, HashFunc>* pht)
		:_node(node)
		,_pht(pht)
	{}

    //...
};

注意:前置声明的参数不能带缺省值

第二个问题,我们使用前置声明之后,_pht确实能实例化了,但是它的_table成员是私有的,在迭代器类中不可使用,所以我们需要在哈希表类中进行迭代器类的友元声明

cpp 复制代码
template<class K, class T, class KeyOfT, class HashFunc = DefaultHashFunc<K>>
class HashTable
{
	typedef HashNode<T> Node;

	//友元声明。类模板也需要写
	template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc>
	friend struct __Hash_iterator;

public:
	typedef __Hash_iterator<K, T, T&, T*, KeyOfT> iterator;
	typedef __Hash_iterator<K, T, const T&, const T*, KeyOfT> const_iterator;
    
    //...
};

注意:和前置声明一样不能有缺省值

unordered_set和unordered_map中迭代器的实现

unordered_set

为了防止set的Key值被修改,这里我们将它的普通迭代器也设置为const迭代器

cpp 复制代码
template<class K>
class UnOrderSet
{
	struct SetKeyOfT
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
public:
	typedef typename Hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator iterator;
	typedef typename Hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;

	iterator begin() const
	{
		return _t.begin();
	}

	iterator end() const
	{
		return _t.end();
	}

    //...
private:
	Hash_bucket::HashTable<K, K, SetKeyOfT> _t;
};

unordered_map

和unordered_set一样为防止set被修改,这里我们也做了细节处理,将pair类型中的K加上了const,这样来既防止Key值被修改了又可以通过迭代器来修改value值

cpp 复制代码
template<class K, class V>
class UnOrderMap
{
	struct MapKeyOfT
	{
		const K& operator()(const pair<K, V>& key)
		{
			return key.first;
		}
	};
public:
	typedef typename Hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
	typedef typename Hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;

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

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

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

	const_iterator end() const
	{
		return _t.end();
	}
    
    //...
private:
	Hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT> _t;
};

unrodered_map和unordered_set insert实现

unordered_set

cpp 复制代码
pair<iterator, bool> insert(const K& key)
{ 
	pair<typename Hash_bucket::HashTable<K, K, SetKeyOfT>::iterator, bool> ret = _t.insert(key);
	return pair<iterator, bool>(ret.first, ret.second); //利用pair的构造函数
}

其实还有第二种写法:

cpp 复制代码
pair<iterator, bool> insert(const K& key)
{ 
	return _t.insert(key); //使用这种方法需要在迭代器中实现(普通迭代器转const迭代器)
}

这里涉及到一个类型转换,普通迭代器转换为const迭代器,实现这个功能需要我们在迭代器类中创建一个特殊的构造函数

便于理解,演示一段代码,为以上图示的简易版本:

cpp 复制代码
template< class T, class Ref>
class A
{
public:
	A(Ref a)
		:_a(a)
	{}

private:
	int _a; 
};

int main()
{
	int x = 2;
	A<int, int&> a1(x);
	A<int, const int&> a2(2);
	A<int, const int&> a3 = a1;

	return 0;
}

加上这段代码之后,就可与实现普通迭代器和const迭代器的转换了

cpp 复制代码
template< class T, class Ref>
class A
{
public:
	A(Ref a)
		:_a(a)
	{}

	A(const A<T, T&>& a) //参数是对象
		:_a(a._a)
	{}


	int _a;
};

int main()
{
	int x = 2;
	A<int, int&> a1(x);
	A<int, const int&> a2(2);
	A<int, const int&> a3 = a1;

	return 0;
}

注意:我们需要将_a设置为共有,因为 A<T, T&>模板类实例化之后是一个新的类,在这个类中使用A类的私有成员会报错

unrodered_map

cpp 复制代码
pair<iterator, bool> insert(const pair<K, V>& key)
{
	return _t.insert(key);
}

unordered_map operator[]的实现

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

使用一个哈希表封装unordered_map和unorder_set整体代码:

hash.h

cpp 复制代码
//确保取余运算符两边是正整数,下标不能是负整数
template<class K>
struct DefaultHashFunc
{
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};

//模板的特化, 让哈希表可以使用string类型
template<>
struct DefaultHashFunc<string>
{
	size_t operator()(const string& key)
	{
		size_t hash = 0;
		for (auto ch : key)
		{
			//使用上一个ASCII码乘以131,避免得到相同的值,注意并不能完全避免,如果数据量大还是会有相同的数
			hash = hash * 131 + ch;
		}

		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 HashFunc>
	class HashTable;

	//哈希表迭代器(单向迭代器)
	template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc = DefaultHashFunc<K>>
	struct __Hash_iterator
	{
		typedef HashNode<T> Node;
		typedef __Hash_iterator<K, T, Ref, Ptr, KeyOfT, HashFunc> self;
		typedef __Hash_iterator<K, T, T&, T*, KeyOfT> iterator;
		typedef __Hash_iterator<K, T, const T&, const T*, KeyOfT> iterator;

		Node* _node;
		//HashTable<K, T, KeyOfT, HashFunc>* _pht;
		const HashTable<K, T, KeyOfT, HashFunc>* _pht;
		//为了可以使用const迭代器我们使用const类型接收
		__Hash_iterator(const iterator& it)
			:_node(it._node)
			,_pht(it._pht)
		{}

		__Hash_iterator(Node* node, const HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			,_pht(pht)
		{}

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

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

		self& operator++()
		{
			if (_node->next)
			{
				_node = _node->next;
				return *this;
			}
			else
			{
				KeyOfT kot;
				HashFunc kov;

				size_t Hashi = kov(kot(_node->_data)) % _pht->_table.size();
				++Hashi;
				while (Hashi < _pht->_table.size())
				{
					if (_pht->_table[Hashi])
					{
						_node = _pht->_table[Hashi];
						return *this;
					}
					++Hashi;
				}
			}
			_node = nullptr;
			return *this;
		}

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

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

	size_t GetNextPrime(size_t prime)
	{
		static const int PRIMECOUNT = 28;
		static const unsigned long primeList[PRIMECOUNT] =
		{
		  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
		};

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

		return primeList[i];
	}

	template<class K, class T, class KeyOfT, class HashFunc = DefaultHashFunc<K>>
	class HashTable
	{
		typedef HashNode<T> Node;

		//友元声明。类模板也需要写,注意和前置声明一样不能有缺省值
		template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc>
		friend struct __Hash_iterator;

	public:
		typedef __Hash_iterator<K, T, T&, T*, KeyOfT> iterator;
		typedef __Hash_iterator<K, T, const T&, const T*, KeyOfT> const_iterator;

		iterator begin()
		{
			size_t Hashi = 0;
			while (_table[Hashi] == nullptr)
				++Hashi;

			return iterator(_table[Hashi], this);
		}

		const_iterator begin() const
		{
			size_t Hashi = 0;
			while (_table[Hashi])
				++Hashi;

			return const_iterator(_table[Hashi], this);
		}

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

		const_iterator end() const
		//const * const this
		{
			//这里的this指针被加了const,我们手动加上的const是修饰this指向的内容,所以在迭代器类中我们需要使用一个const类型的参数接收,不然会导致类型不一致
			//如果const修饰的this指针本身的话就没事,因为在迭代器类中我们只需要去读它并不需要去写它
			return const_iterator(nullptr, this);
		}

		HashTable()
		{
			_table.resize(10, nullptr);
		}

		HashTable(const HashTable<K, T, KeyOfT, HashFunc>& H)
		{
			HashFunc kov;
			KeyOfT kot;
			size_t n = H._table.size();
			_table.resize(n);

			for (size_t i = 0; i < n; ++i)
			{
				Node* cur = H._table[i];
				while (cur)
				{
					size_t Hashi = kov(kot(cur->_data)) % n;
					Node* newnode = new Node(H._table[Hashi]->_data);
					//头插
					newnode->next = _table[i];
					_table[i] = newnode;

					cur = cur->next;
				}
			}
		}

		~HashTable()
		{
				for (size_t i = 0; i < _table.size(); ++i)
				{
					Node* cur = _table[i];
					while (cur)
					{
						Node* next = cur->next;
						delete cur;
						cur = next;
					}

					_table[i] = nullptr;
				}
		}

		iterator Find(const K& key)
		{
			HashFunc kov;
			KeyOfT kot;

			size_t Hashi = kov(key) % _table.size();

			Node* cur = _table[Hashi];
			while (cur != nullptr)
			{
				Node* next = cur->next;

				if (kot(cur->_data) == key)
					return iterator(cur, this);

				cur = next;
			}

			return end();
		}

		pair<iterator, bool> insert(const T& data)
		{
			HashFunc kov;
			KeyOfT kot;

			if (Find(kot(data))._node)
				return make_pair(Find(kot(data)), false);
			//负载因子处理扩容
			//为1进行扩容
			if (_n == _table.size())
			{
				size_t newSize = GetNextPrime(_table.size());
				vector<Node*> newtable;
				newtable.resize(newSize, nullptr);
				
				//哈希桶上链接的结点位置是任意的,头插,尾插都可以
				for(size_t i = 0; i < _table.size(); ++i)
				{
					Node* cur = _table[i];
					//将cur上哈希桶的每个结点都给链接到_table上
					//将旧哈希桶上的元素重新映射到新哈希表上
					while (cur)
					{
						Node* next = cur->next;
						size_t Hashi = kov(kot(cur->_data)) % newSize;

						//头插操作,将cur理解为一个新结点头插到这个newtable上
						cur->next = newtable[Hashi];
						newtable[Hashi] = cur;

						cur = next;
					}
				}
				_table.swap(newtable);
			}

			size_t hashi = kov(kot(data)) % _table.size();

			//头插
			Node* newnode = new Node(data);
			newnode->next = _table[hashi];
			_table[hashi] = newnode;
			++_n;
			
			return make_pair(iterator(newnode, this), true);
		}

		bool erase(const K& key)
		{
			if (Find(key) == nullptr)
				return false;

			HashFunc kov;
			KeyOfT kot;

			size_t Hashi = kov(key) % _table.size();

			Node* cur = _table[Hashi];
			Node* prev = nullptr;

			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev == nullptr)
					{
						_table[Hashi] = cur->next;
					}
					else
					{
						prev->next = cur->next;
					}
					delete cur;
					return true;
				}

				prev = cur;
				cur = cur->next;
			}

			return false;
		}

	private:
		vector<Node*> _table;
		size_t _n = 0;
	};
}

unordered_set

cpp 复制代码
namespace HashSet
{
	template<class K>
	class UnOrderSet
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename Hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename Hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;

		iterator begin() const
		{
			return _t.begin();
		}

		iterator end() const
		{
			return _t.end();
		}

		//pair<const_iterator, bool>
		pair<iterator, bool> insert(const K& key)
		{ 
			//return _t.insert(key); //使用这种方法需要在迭代器中实现(普通迭代器转const迭代器)
			//模板实例化只需要类型就可以
			pair<typename Hash_bucket::HashTable<K, K, SetKeyOfT>::iterator, bool> ret = _t.insert(key);
			return pair<iterator, bool>(ret.first, ret.second); //利用pair的构造函数
		}

	private:
		Hash_bucket::HashTable<K, K, SetKeyOfT> _t;
	};
}

unordered_map

cpp 复制代码
namespace HashMap
{
	template<class K, class V>
	class UnOrderMap
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& key)
			{
				return key.first;
			}
		};
	public:
		typedef typename Hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename Hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;

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

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

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

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

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

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

		void Print()
		{
			_t.Print();
		}

	private:
		Hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT> _t;
	};
}
相关推荐
爱数学的程序猿10 分钟前
Python入门:1.Python介绍
开发语言·python
γ..11 分钟前
基于MATLAB的图像增强
开发语言·深度学习·神经网络·学习·机器学习·matlab·音视频
小王爱吃月亮糖21 分钟前
C++进阶-1-单继承、多继承、虚继承
开发语言·c++·笔记·学习·visual studio
落魄君子37 分钟前
SVM分类-支持向量机(Support Vector Machine)
神经网络·算法·支持向量机·分类
m0_6075487641 分钟前
什么是单例模式
开发语言·javascript·单例模式
Am心若依旧40942 分钟前
[c++进阶(三)]单例模式及特殊类的设计
java·c++·单例模式
小王爱吃月亮糖43 分钟前
补充--C++的项目结构和管理
数据结构·c++·笔记·学习
因特麦克斯44 分钟前
如何实现对象的克隆?如何实现单例模式?
c++·单例模式
CSND7401 小时前
Ubuntu vi(vim)编辑器配置一键补全main函数
linux·c语言·ubuntu·编辑器·vim
上理考研周导师2 小时前
【单片机原理】第1章 微机基础知识,运算器,控制器,寄存器,微机工作过程,数制转换
算法