【C++】unordered_map、unordered_set 模拟实现

news2024/11/26 12:44:58

文章目录

  • 概念
  • 框架
  • 实现
    • 正反迭代器
    • Find()、Insert() 、Erase()
    • unordered_map 的 operator[ ]
  • 源代码
    • HashTable.h
    • unordered_map.h
    • unordered_set.h

概念

unordered_set 是含有 Key 类型唯一对象集合的关联容器。搜索、插入和移除拥有平均常数时间复杂度。在内部,元素并不以任何特别顺序排序,而是组织进桶中。元素被放进哪个桶完全依赖其值的哈希。这允许对单独元素的快速访问,因为哈希一旦确定,就准确指代元素被放入的桶。不可修改容器元素(即使通过非 const 迭代器),因为修改可能更改元素的哈希,并破坏容器。

unordered_map 是关联容器,含有带唯一键的 “键-值” pair 。搜索、插入和元素移除拥有平均常数时间复杂度。元素在内部不以任何特定顺序排序,而是组织进桶中。元素放进哪个桶完全依赖于其键( Key) 的哈希。这允许对单独元素的快速访问,因为一旦计算哈希,则它准确指代元素所放进的桶。

读上面的两断话会显得非常抽象,但是,如果对模拟实现 Map、Set 有一定了解(不了解可以看一下这篇文章 Map、Set模拟实现 ),那么就能很快明白,只不过是 原本用红黑树作为底层数据结构来实现 Map、Set ,现在使用哈希(拉链法)作为底层数据结构来实现 unordered_map、unordered_set 。既然是使用的哈希,那么存储的数据就无法像红黑树一样具有严格的顺序,但是,换来的确是查找效率的极大提高,提高为 O(1) 的查找效率。

框架

如下图,哈希桶本质上是一个 vector ,容器里面存放的是 HashNode 这个节点的指针,节点是一个结构体,结构体里面存储着下一个 HashNode 结构体的指针当前节点的数据

哈希桶的实现自然不必多说,不了解的话可以查看这篇文章:C++ 哈希。这里是使用拉链法解决哈希冲突。
在这里插入图片描述

如下,必然要定义一个 HashNode 结构体,HashFunc 是为了根据 Key 来计算出数据的哈希值,以便得知数据存放在哪一个桶里面,至于 正反向迭代器自然不必多说,必有的配置,最后就是 哈希桶了,其成员变量一个是 _tables ,另一个是用来表示 _tables 中的有效数据个数。


#pragma once
#include<iostream>
#include<vector>

using namespace std;

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;
		}
	};

	// 模板的特化,如果有 string 类型的数据,优先使用特化的模板
	template<>
	struct HashFunc<string>
	{
		size_t operator()(const string& s)
		{
			size_t hash = 0;
			for (auto& a : s)
			{
				hash += a;
				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=HashFunc<K>>
	struct Iterator
	{
	public:
		typedef HashNode<T> Node;
		typedef Iterator<K, T, Ref, Ptr, KeyOfT, Hash> self;
		typedef HashTable<K, T, KeyOfT, Hash> HT;
	public:
		Iterator(Node* node = nullptr, HT* tables= nullptr)
			:_node(node)
			,_ht(tables)
		{}

		Iterator(const Iterator<K,T,T&,T*,KeyOfT,Hash>& it)
			:_node(it._node)
			,_ht(it._ht)
		{}

		Node* _node;
		HT* _ht;
	};


	template<class K,class T,class Ref,class Ptr,class KeyOfT,class Hash=HashFunc<K>>
	struct ReverseIterator
	{
		
		typedef HashNode<T> Node;
		typedef ReverseIterator<K, T, Ref, Ptr, KeyOfT, Hash> self;
		typedef HashTable<K, T, KeyOfT, Hash> HT;

		ReverseIterator(Node* node, HT* ht)
		{
			it._node = node;
			it._ht = ht;
		}

		ReverseIterator(const Iterator<K, T,Ref,Ptr,KeyOfT,Hash> rit)
		{
			it._node = rit._node;
			it._ht = rit._ht;
		}

		ReverseIterator(const ReverseIterator<K, T, T&, T*, KeyOfT, Hash>& rit)
		{
			it._node = rit.it._node;
			it._ht = rit.it._ht;
		}


		Iterator<K, T, Ref, Ptr, KeyOfT, Hash> it;
	};

	template<class K, class T, class KeyOfT,class Hash = HashFunc<K>>
	class HashTable
	{
	public:

		typedef HashNode<T> Node;

		typedef Iterator<K, T, T&, T*, KeyOfT, Hash> iterator;
		typedef ReverseIterator<K, T, T&, T*, KeyOfT, Hash> reverse_iterator;

		// 模板类友元,声明不需要默认参数
		template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
		friend struct Iterator;

	public:
		//	成员方法

	private:
		vector<HashNode<T>*> _tables;
		size_t n = 0; // vector 存储有效数据个数
	};

}

对于迭代器、HashTable 的模板参数中, KeyOfT 的作用,和模拟实现 Map、Set 一样,就是为了取得数据的 Key 。因为 unordered_map 存储的是 pair<K,V> ,unordered_set 存储的是 K,为了使 HashTable 可以同时支持它们两个容器,所以有了 KeyOfT,该参数的传参是在 unordered_map 和 unordered_set 里面实现的。

如下,在 unordered_map 里面实现了 KeyOfMap,然后将仿函数传入 HashTable 的对应参数,这样在 HashTable 中,用 KeyOfT 实例化出的 kot 对象,实际上就是 struct KeyOfMap 的对象,再使用 kot 调用重载的 () 就可以拿到 pair<K,V> 中的 K 。
unordered_set 同理,不多赘述。

#include"HashTable.h"

namespace simulate_unorderedmap
{
	template<class K, class V,class Hash=HashBucket::HashFunc<K>>
	class unordered_map
	{
	public:

		struct KeyOfMap
		{
			const K& operator()(const pair<const K, V>& kv)
			{
				return kv.first;
			}
		};

	public:
    // 成员函数

	private:
		HashBucket::HashTable<K, pair<const K, V>, KeyOfMap,Hash> _map;
	};

}

实现

正反迭代器

这里可以使用包装器的设计模式,实现正向迭代器,然后反向迭代器实际上就是对正向迭代器的封装,反向迭代器的++,就是正向迭代器的–。

如下, Iterator 中有两个成员变量,一个是 HashNode 节点的指针,另一个是 HashTable 的指针。需要 _ht 这个成员变量的原因,其实是进行 ++ 、-- 所需要的。

对于成员函数的介绍主要就在 ++ 、-- 这两个。如下图, 我们可以理解为,vector 容器里面装的是一个个桶,桶是链表的结构。进行一次 ++ ,那么迭代器自然是指向链表中下一个节点,此时分为两种情况,下一个节点存在(如下图左边),下一个节点不存在,即当前节点已经是当前桶的最后一个节点(如下图右边)。 对于前者,自然很容易,但是,对于后者,就需要找到下一个有数据的桶,然后将 _node 指针指向桶内第一个节点,具体步骤如下。

  • 1、得到 key。 kot(_node->_data)
  • 2、通过 hash 函数得到 hash 值。 hash(kot(_node->_data))
  • 3、得到桶号(一般都为 hash 值对桶数求模)。size_t hashi = hash(kot(_node->_data)) % _ht->_tables.size();
  • 4、找到下一个有数据的桶。

要进行上面的操作,必须要有当前的 HashTable,但是用一整个 HashTable 当作成员变量,消耗太大,所以用指针。
在这里插入图片描述

operator- - () 也是类似的实现,只不过 - - 要先经过前三步得到桶号,然后判断当前节点的前面有没有节点,可以根据桶的第一个节点是不是当前节点进行判断,不难理解。

同时,无论是 ++ 还是 - - 都要注意边界条件,最后一个哈希桶的最后一个数据,进行 ++ 只能是 nullptr, 同样,第一个哈希桶的第一个数据,进行 - - 也只能是 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=HashFunc<K>>
	struct Iterator
	{
	public:
		typedef HashNode<T> Node;
		typedef Iterator<K, T, Ref, Ptr, KeyOfT, Hash> self;
		typedef HashTable<K, T, KeyOfT, Hash> HT;
	public:
		Iterator(Node* node = nullptr, HT* tables= nullptr)
			:_node(node)
			,_ht(tables)
		{}

		Iterator(const Iterator<K,T,T&,T*,KeyOfT,Hash>& it)
			:_node(it._node)
			,_ht(it._ht)
		{}

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

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

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

		self& operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
				return *this;
			}

			// 找到下一个有数据的桶
			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;
		}

		self& operator--()
		{
			// 找到当前桶
			KeyOfT kot;
			Hash hash;
			int hashi = hash(kot(_node->_data)) % _ht->_tables.size();
			
			// 桶的第一个就是当前节点构成的迭代器,往前面的桶找
			if (_ht->_tables[hashi] == _node)
			{
				--hashi;
				while (hashi >= 0)
				{
					if (_ht->_tables[hashi] == nullptr)
					{
						--hashi;
					}
					else
					{
						// 找到上一个有数据的桶的最后一个数据
						Node* cur = _ht->_tables[hashi];
						while (cur->_next)
							cur = cur->_next;
						_node = cur;
						break;
					}
				}

				if (hashi < 0)
					_node = nullptr;
			}
			else // 桶的第一个不是当前节点构成的迭代器,在该桶里面找
			{
				Node* cur = _ht->_tables[hashi];
				while (cur->_next != _node)
				{
					cur = cur->_next;
				}
				_node = cur;
			}

			return *this;
		}

		Node* _node;
		HT* _ht;
	};

至于反向迭代器,那就是对正向迭代器的封装。

template<class K,class T,class Ref,class Ptr,class KeyOfT,class Hash=HashFunc<K>>
	struct ReverseIterator
	{
		
		typedef HashNode<T> Node;
		typedef ReverseIterator<K, T, Ref, Ptr, KeyOfT, Hash> self;
		typedef HashTable<K, T, KeyOfT, Hash> HT;

		ReverseIterator(Node* node, HT* ht)
		{
			it._node = node;
			it._ht = ht;
		}

		ReverseIterator(const Iterator<K, T,Ref,Ptr,KeyOfT,Hash> rit)
		{
			it._node = rit._node;
			it._ht = rit._ht;
		}

		ReverseIterator(const ReverseIterator<K, T, T&, T*, KeyOfT, Hash>& rit)
		{
			it._node = rit.it._node;
			it._ht = rit.it._ht;
		}

		self operator++()
		{
			return --it;
		}

		self& operator--()
		{
			return ++it;
		}

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

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

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

		Iterator<K, T, Ref, Ptr, KeyOfT, Hash> it;
	};

Find()、Insert() 、Erase()

如下是 unordered_map、unordered_set 里面的 Find() 、Erase() 、Insert() 方法,其实就是对 HashTable 的封装。知道了 HashTable 里面的 Find() 、insert() 、Erase() 实现原理即可,并不难,都是和 ++ 类似的思路。

#include"HashTable.h"

namespace simulate_unorderedmap
{
	template<class K, class V,class Hash=HashBucket::HashFunc<K>>
	class unordered_map
	{
	public:

		struct KeyOfMap
		{
			const K& operator()(const pair<const K, V>& kv)
			{
				return kv.first;
			}
		};

	public:
		typedef typename HashBucket::HashNode<pair<const K, V>> Node;

		typedef typename HashBucket::HashTable<K, pair<const K, V>, KeyOfMap, Hash>::iterator iterator;
		typedef typename HashBucket::HashTable<K, pair<const K, V>, KeyOfMap, Hash>::const_iterator const_iterator;
		typedef typename HashBucket::HashTable<K, pair<const K, V>, KeyOfMap, Hash>::reverse_iterator reverse_iterator;
		typedef typename HashBucket::HashTable<K, pair<const K, V>, KeyOfMap, Hash>::const_reverse_iterator const_reverse_iterator;

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

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

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

		iterator Find(const K& key)
		{
			Node* cur = _map.Find(key);
			return iterator(cur, &_map);
		}

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

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


	private:
		HashBucket::HashTable<K, pair<const K, V>, KeyOfMap,Hash> _map;
	};

}

unordered_map 的 operator[ ]

unordered_map 的 [ ] 是兼具插入查找功能的。unordered_map 里面存储的是 pair<K,V> ,假设有一个 unordered_map< int,int >对象 m,执行 m[10]=1000; —— 如果存储的 pair<K,V> 里面,有 K 类型的数据是 10,那么就将这个键值对的 V 修改成 100;没有则插入 make_pair(10,100);

要完成上面描述的功能,就必须要让 [ ] 的返回值是 V& (V的引用),这样才可以修改 V 类型的数据。 可以通过迭代器来实现!!

如下是 unordered_map 里面实现 operator[ ] 的代码,_map 是 HashTable 实例化出来的对象,其 Insert() 返回值是 pair<iterator,bool> , 很明显,返回值包含两个信息:

  • 1.某个节点构成的迭代器。如果插入成功,那么返回插入节点封装成的迭代器。如果插入失败(已经有了 K 类型对应的数据),那么就相当于查找功能,返回找到的节点封装成的迭代器
  • 2.是否插入成功,插入成功返回 true,否则false。
		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = _map.insert(make_pair(key, V()));
			return ret.first->second;
		}

源代码

HashTable.h

#pragma once
#include<iostream>
#include<vector>

using namespace std;

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;
		}
	};

	// 模板的特化,如果有 string 类型的数据,优先使用特化的模板
	template<>
	struct HashFunc<string>
	{
		size_t operator()(const string& s)
		{
			size_t hash = 0;
			for (auto& a : s)
			{
				hash += a;
				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=HashFunc<K>>
	struct Iterator
	{
	public:
		typedef HashNode<T> Node;
		typedef Iterator<K, T, Ref, Ptr, KeyOfT, Hash> self;
		typedef HashTable<K, T, KeyOfT, Hash> HT;
	public:
		Iterator(Node* node = nullptr, HT* tables= nullptr)
			:_node(node)
			,_ht(tables)
		{}

		Iterator(const Iterator<K,T,T&,T*,KeyOfT,Hash>& it)
			:_node(it._node)
			,_ht(it._ht)
		{}

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

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

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

		self& operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
				return *this;
			}

			// 找到下一个有数据的桶
			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;
		}

		self& operator--()
		{
			// 找到当前桶
			KeyOfT kot;
			Hash hash;
			int hashi = hash(kot(_node->_data)) % _ht->_tables.size();
			
			// 桶的第一个就是当前节点构成的迭代器,往前面的桶找
			if (_ht->_tables[hashi] == _node)
			{
				--hashi;
				while (hashi >= 0)
				{
					if (_ht->_tables[hashi] == nullptr)
					{
						--hashi;
					}
					else
					{
						// 找到上一个有数据的桶的最后一个数据
						Node* cur = _ht->_tables[hashi];
						while (cur->_next)
							cur = cur->_next;
						_node = cur;
						break;
					}
				}

				if (hashi < 0)
					_node = nullptr;
			}
			else // 桶的第一个不是当前节点构成的迭代器,在该桶里面找
			{
				Node* cur = _ht->_tables[hashi];
				while (cur->_next != _node)
				{
					cur = cur->_next;
				}
				_node = cur;
			}

			return *this;
		}

		Node* _node;
		HT* _ht;
	};


	template<class K,class T,class Ref,class Ptr,class KeyOfT,class Hash=HashFunc<K>>
	struct ReverseIterator
	{
		
		typedef HashNode<T> Node;
		typedef ReverseIterator<K, T, Ref, Ptr, KeyOfT, Hash> self;
		typedef HashTable<K, T, KeyOfT, Hash> HT;

		ReverseIterator(Node* node, HT* ht)
		{
			it._node = node;
			it._ht = ht;
		}

		ReverseIterator(const Iterator<K, T,Ref,Ptr,KeyOfT,Hash> rit)
		{
			it._node = rit._node;
			it._ht = rit._ht;
		}

		ReverseIterator(const ReverseIterator<K, T, T&, T*, KeyOfT, Hash>& rit)
		{
			it._node = rit.it._node;
			it._ht = rit.it._ht;
		}

		self operator++()
		{
			return --it;
		}

		self& operator--()
		{
			return ++it;
		}

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

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

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

		Iterator<K, T, Ref, Ptr, KeyOfT, Hash> it;
	};

	template<class K, class T, class KeyOfT,class Hash = HashFunc<K>>
	class HashTable
	{
	public:

		typedef HashNode<T> Node;

		typedef Iterator<K, T, T&, T*, KeyOfT, Hash> iterator;
		typedef Iterator<K, T, const T&, const T*, KeyOfT, Hash> const_iterator;
		typedef ReverseIterator<K, T, T&, T*, KeyOfT, Hash> reverse_iterator;
		typedef ReverseIterator<K, T, const T&, const T*, KeyOfT, Hash> const_reverse_iterator;

		// 模板类友元,声明不需要默认参数
		template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
		friend struct Iterator;

	public:

		iterator begin()
		{
			Node* cur = nullptr;
			for (int 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 (int 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);
		}


		reverse_iterator rbegin()
		{
			int hashi = _tables.size() - 1;
			while (_tables[hashi] == nullptr && hashi>=0)
			{
				hashi--;
			}
			if (hashi < 0)
				return reverse_iterator(nullptr, this);

			// 最后一个
			Node* cur = _tables[hashi];
			while (cur->_next)
				cur = cur->_next;
			return reverse_iterator(cur, this);
		}

		reverse_iterator rend()
		{
			return reverse_iterator(nullptr, this);
		}


		const_reverse_iterator rbegin()const
		{
			int hashi = _tables.size() - 1;
			while (_tables[hashi] == nullptr && hashi >= 0)
			{
				hashi--;
			}
			if (hashi < 0)
				return const_reverse_iterator(nullptr, this);

			// 最后一个
			Node* cur = _tables[hashi];
			while (cur->_next)
				cur = cur->_next;
			return const_reverse_iterator(cur, this);
		}

		const_reverse_iterator rend()const
		{
			return const_reverse_iterator(nullptr, this);
		}

		bool Erase(const K& key)
		{
			KeyOfT kot;
			Hash hash;
			size_t hashi = hash(key) % _tables.size();
			Node* cur = _tables[hashi];
			Node* prev = nullptr;
			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;
		}

		Node* Find(const K& key)
		{
			if (_tables.size() == 0)
				return nullptr;

			Hash hash;
			KeyOfT kot;
			size_t hashi = hash(key) % _tables.size();

			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
					return cur;
				cur = cur->_next;
			}
			return nullptr;
		}


		size_t GetNextPrime(size_t prime)
		{
			// SGI
			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
			};

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

			return __stl_prime_list[i];
		}

		pair<iterator,bool> insert(const T& data)
		{
			KeyOfT kot;
			Node* findnode = Find(kot(data));
			if (findnode)
				return make_pair(iterator(findnode, this), false);

			Hash hash;

			if (_tables.size() == n)
			{
				//size_t newsize = _tables.size() == 0 ? 10 : _tables.size() * 2;

				size_t newsize = GetNextPrime(_tables.size());

				vector<Node*> newtables(newsize, nullptr);
				for (auto& cur : _tables)
				{
					while (cur) // 链表节点全部插入newht
					{
						Node* next = cur->_next;
						size_t newhashi = hash(kot(cur->_data)) % newsize;

						cur->_next = newtables[newhashi];
						newtables[newhashi] = cur;

						cur = next;
					}
				}

				_tables.swap(newtables);
			}

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

			++n;
			return make_pair(iterator(tmp,this),true);

		}


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

				if (tmp > max)
					max = tmp;
			}

			return max;
		}

	private:
		vector<HashNode<T>*> _tables;
		size_t n = 0; // vector 存储有效数据个数
	};

}

unordered_map.h

#include"HashTable.h"



namespace simulate_unorderedmap
{
	template<class K, class V,class Hash=HashBucket::HashFunc<K>>
	class unordered_map
	{
	public:

		struct KeyOfMap
		{
			const K& operator()(const pair<const K, V>& kv)
			{
				return kv.first;
			}
		};

	public:
		typedef typename HashBucket::HashNode<pair<const K, V>> Node;

		typedef typename HashBucket::HashTable<K, pair<const K, V>, KeyOfMap, Hash>::iterator iterator;
		typedef typename HashBucket::HashTable<K, pair<const K, V>, KeyOfMap, Hash>::const_iterator const_iterator;
		typedef typename HashBucket::HashTable<K, pair<const K, V>, KeyOfMap, Hash>::reverse_iterator reverse_iterator;
		typedef typename HashBucket::HashTable<K, pair<const K, V>, KeyOfMap, Hash>::const_reverse_iterator const_reverse_iterator;

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

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

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

		iterator Find(const K& key)
		{
			Node* cur = _map.Find(key);
			return iterator(cur, &_map);
		}

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

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


	private:
		HashBucket::HashTable<K, pair<const K, V>, KeyOfMap,Hash> _map;
	};

}

unordered_set.h

#include"HashTable.h"

namespace simulate_unorderedset
{

	template<class K, class Hash = HashBucket::HashFunc<K>>
	class unordered_set
	{
	public:
		typedef typename HashBucket::HashNode<K> Node;
		struct KeyOfSet
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

	public:

		typedef typename HashBucket::HashTable<K, K, KeyOfSet, Hash>::const_iterator iterator;
		typedef typename HashBucket::HashTable<K, K, KeyOfSet, Hash>::const_iterator const_iterator;
		typedef typename HashBucket::HashTable<K, K, KeyOfSet, Hash>::const_reverse_iterator reverse_iterator;
		typedef typename HashBucket::HashTable<K, K, KeyOfSet, Hash>::const_reverse_iterator const_reverse_iterator;

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

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

		iterator Find(const K& key)
		{
			Node* cur=_set.Find(key);
			return iterator(cur, &_set);
		}

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

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

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

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

		reverse_iterator rbegin()
		{
			return _set.rbegin();
		}

		reverse_iterator rend()
		{
			return _set.rend();
		}


	private:
		HashBucket::HashTable<K, K, KeyOfSet,Hash> _set;
	};

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/742784.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTML页面通过Web3JS连接智能合约并调用其中接口

之前我们学习solidity 并用它编写了智能合约 登上区块链 然后也做了基本的测试 但是 其实在web3时代 前端依旧扮演者非常重要的角色 我们现在就来打通web3 从合约到页面的一个管理 首先 我们还是将自己的ganache环境起起来 然后 在我们之前智能合约的项目终端执行 truffle m…

Python-Go 文件操作和异常操作

目录 python的异常操作 异常是什么&#xff1f; 错误与异常的区别&#xff1a; 常见异常 Traceback 错误回溯 异常处理 异常处理的定义&#xff08;try - except&#xff09; 扩展&#xff08;可以捕获不同的错误、多except语句&#xff09;&#xff08;else - finall…

PFC离散元仿真,3DEC非连续岩石力学与结构问题分析

一、背景&#xff1a; 随着我国经济的发展&#xff0c;岩土工程涉及的要求从材料、理论到施工工艺都提出了全方位的系统升级。在岩土工程分析设计中&#xff0c;3DEC和PFC软件快速建模也一直是岩土工作者所关注的问题。3DEC是非连续岩石力学与结构问题的首选分析程序&#xff0…

C语言图书管理系统

一&#xff0c;开发环境 操作系统&#xff1a;windows10, windows11, linux, mac等。开发工具&#xff1a;Qt, vscode, visual studio等开发语言&#xff1a;c 二&#xff0c;功能需求 1. 图书信息管理&#xff1a; 这个功能的主要任务是保存和管理图书的所有信息。这应该包…

C++数据结构X篇_09_C++实现栈的顺序存储与链式存储

本篇参考C实现队列的顺序存储与链式存储整理&#xff0c;先搞懂结构框架&#xff0c;后期根据视频利用c对内容实现&#xff0c;也可以对c有更高的提升。 文章目录 1. 顺序存储2. 链式存储 队列是一种特殊的数据存储结构&#xff0c;与栈不同的是其数据存储与访问顺序为先进先出…

Python教程:全局变量和局部变量

变量的作用域始终是Python学习中一个必须理解掌握的环节&#xff0c;下面我们从局部变量和全局变量开始全面解析Python中变量的作用域。 所谓局部变量&#xff0c;指的是定义在函数内的变量。定义在函数内的变量&#xff0c;只能在函数内使用&#xff0c;它与函数外具有相同名…

GlasgowSmile-v2靶机复盘

GlasgowSmile-v2靶机复盘 这是一个非常难的靶机 这个靶机是直接给我们ip地址的&#xff0c;所以就不用去找ip地址了&#xff0c;直接对ip进行一个扫描。 发现开通了22&#xff0c;80&#xff0c;83&#xff0c;但是8080端口是个开启我们无法访问的状态&#xff0c;所以可以猜…

【Redis】—— Redis的RDB持久化机制

&#x1f4a7; 【 R e d i s 】—— R e d i s 的 R D B 持久化机制 \color{#FF1493}{【Redis】 —— Redis的RDB持久化机制} 【Redis】——Redis的RDB持久化机制&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风撞…

Springboot -- Mybatis + Mybatis Generate + KingbaseES8(pgsql) + 静态多数据源

&#x1f3db;&#x1f3db;&#x1f3db; 以下内容记录一次 Springboot 项目整和人大金仓数据库的过程 &#x1f3db;&#x1f3db;&#x1f3db; 文章目录 KingBaseES8SpringBoot整合KES8(pgsql)修改pom文件配置多数据源配置文件配置类 代码生成器配置JsonTypeHandler修改配…

[HDCTF2019]Maze(初识逆向)

下载附件解压&#xff0c;为了方便&#xff0c;我将文件名改为了maze.exe 一般我们先查壳&#xff0c;确实存在UPX壳 对于刚接触逆向的我&#xff0c;一键脱掉哈哈哈 不过还是要说一下&#xff0c;这种脱壳处理有可能会产生其它损坏或者影响&#xff0c;需要进行一定的修复 可…

Visio使用

1.Visio中字母上下标快捷键&#xff1a; 选中要成为上标的文字,ctrlshift"" 选中要成为下标的文字,ctrl"" 2.导入CAD文件 从 AutoCAD 版本 2007 到 2017 导入 .dwg 或 .dxf 文件。 3.编辑CAD文件 右键图形—CAD绘图对象—转换 选择高级 将默认选项改…

居安思危,测试员被裁了还能干什么?

2019年迎来了一大波大佬级公司裁员&#xff0c;包括了阿里、美团、知乎……。它们是各行业的独角兽公司&#xff0c;既是媒体关注的焦点&#xff0c;也是代表了行业风向的指针。 2019年的互联网&#xff0c;已经过了蓬勃发展期&#xff0c;接下来要迎接的更加平稳、规律的发展…

ECM能耗管理云平台

能源是企业运营中不可或缺的要素之一&#xff0c;直接影响企业的成本和竞争力。能源消耗和碳排放不断增长&#xff0c;对环境和可持续发展造成了严重影响。越来越多的企业开始关注并实施能耗管理云平台系统&#xff0c;以实现更高的能源效率、降低碳排放。 ECM能耗管理云平台系…

数分面试题:魔方涂色

问题&#xff1a;现在有一个正方体&#xff0c;现在有6种颜色&#xff0c;给正方体的每一面涂一种颜色&#xff0c;有多少种涂色方法。 注意&#xff01;正方体是可以旋转的&#xff0c;如果旋转正方体之后&#xff0c;涂色效果和另一种涂色效果相同&#xff0c;那么算是一种涂…

车载开发月薪10k与30k的区别,就是CAN总线技术

CAN&#xff08;Controller Area Network&#xff09;总线是一种实时通信协议&#xff0c;是一种广泛应用于车载电子系统中的网络技术。CAN总线技术最初是由德国Bosch公司开发的&#xff0c;用于汽车电子控制系统&#xff08;ECU&#xff09;之间的通信&#xff0c;它能够在车辆…

Linux的shell脚本

Linux的shell脚本 &#x1f607;博主简介&#xff1a;我是一名正在攻读研究生学位的人工智能专业学生&#xff0c;我可以为计算机、人工智能相关本科生和研究生提供排忧解惑的服务。如果您有任何问题或困惑&#xff0c;欢迎随时来交流哦&#xff01;&#x1f604; ✨座右铭&…

Ai数字人浪潮来袭,超写实数字人系统出世,为企业打造定制化服务

数字人概念首次出现在80年代&#xff0c;最初在音乐领域应用&#xff0c;使用手绘技术进行生成。随着时间的推移&#xff0c;传统手绘被CG和动作捕捉等技术取代&#xff0c;从而使虚拟数字人的制作变得更加简化并降低成本。因此&#xff0c;虚拟数字人产业进入产业化探索阶段。…

SpringBoot快速实践 --Ⅰ

文章目录 启动一个SpringBoot项目如何替换内嵌容器玩转SpringBoot配置全局异常处理过滤器拦截器使用Lombok简洁代码使用IDEA HTTP Client进行接口调试 启动一个SpringBoot项目 如果你觉得使用官网来创建太慢了&#xff0c;那你直接把以前项目的依赖粘过来就行了&#xff1a; …

pytorch LBFGS

LBFGS pytorch的LBFGS也是一个优化器 但是与一般的优化器不同 平常我们的顺序是 losscriterion(predict, gt) optim.zero_grad() loss.backward() optim.step()而LBFGS是 def closure():optim.zero_grad()loss criterion(predict, gt)loss.backward()return lossoptim.step…

5.EFLK(ELK+filebeat)+filter过滤

文章目录 EFLK&#xff08;ELKfilebeat&#xff09;部署filebeat修改配置文件logstash配置 logstash的filter过滤grok(正则捕获插件)内置正则表达式调用自定义表达式 mutate(数据修改插件)重命名字段添加字段删除字段转换数据类型替换字段内容以"|"为分割符拆分数据成…