【C++】STL-哈希表封装unorder_set和unordered_map

news2024/11/16 6:20:14

目录

1、实现哈希表的泛型

2、unordered_set和unordered_map的插入

3、迭代器

3.1 operator++

3.2 const迭代器

 4、find

5、unordered_map的operator[]

6、对于无法取模的类型

7、介绍unordered_set的几个函数

7.1 bucket_count

7.2 bucket_size

7.3 bucket

7.4 reserve


1、实现哈希表的泛型

我们先引入上一节中的哈希表,这里使用的是开散列的哈希表

namespace hash_bucket
{
	template<class K,class V>
	struct HashNode
	{
		pair<K, V> _kv;
		HashNode<K, V>* _next;
		HashNode(const pair<K,V>& kv)
			:_kv(kv)
			,_next(nullptr)
		{}
	};
	template<class K>
	struct HashFunc
	{
		size_t operator()(const K& key)
		{
			return (size_t)key;
		}
	};
	template<> // 对string类型进行特化
	struct HashFunc<string>
	{
		size_t operator()(const string& s)
		{
			size_t hash = 0;
			for (auto e : s)
			{
				hash *= 31;
				hash += e;
			}
			return hash;
		}
	};
	template<class K, class V, class Hash = HashFunc<K>>
	class HashTable
	{
		typedef HashNode<K, V> Node;
	public:
		HashTable()
		{
			_tables.resize(10, nullptr);
		}
		~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;
			}
		}
		bool Insert(const pair<K, V>& kv)
		{
			if (Find(kv.first))
				return false;
			Hash hs;
			size_t hashi = hs(kv.first) % _tables.size();
			// 负载因子==1,扩容
			if (_n == _tables.size())
			{
				vector<Node*> newtables(_tables.size() * 2, nullptr);
				for (size_t i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;
						// 旧表中的结点,挪动到新表重新映射的位置
						size_t hashi = hs(cur->_kv.first) % newtables.size();
						// 头插到新表
						cur->_next = newtables[hashi];
						newtables[hashi] = cur;
						cur = next;
					}
					// 原来的桶取完后要置空
					_tables[i] = nullptr;
				}
				_tables.swap(newtables);
			}
			// 头插
			Node* newNode = new Node(kv);
			newNode->_next = _tables[hashi];
			_tables[hashi] = newNode;
			++_n;
			return true;
		}
		Node* Find(const K& key)
		{
			Hash hs;
			size_t hashi = hs(key) % _tables.size();
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (cur->_kv.first == key)
					return cur;
				cur = cur->_next;
			}
			return nullptr;
		}
		bool Erase(const K& key)
		{
			Hash hs;
			size_t hashi = hs(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (cur->_kv.first == key)
				{
					if (prev == nullptr)
						_tables[hashi] = cur->_next;
					else
						prev->_next = cur->_next;
					delete cur;
					return true;
				}
				prev = cur;
				--_n;
				cur = cur->_next;
			}
			return false;
		}
	private:
		vector<Node*> _tables;
		size_t _n = 0; // 表中存储数据个数
	};
}

在这个哈希表中,结点中默认存储的类型是pair,这是unordered_map中的数据类型,并不符合unordered_set.通过观察STL源码可知,unordered_set和unordered_map使用的是同一个哈希表类模板,所以我们需要对哈希表进行改造,使哈希表可以通过模板参数来控制结点中存放的数据类型

namespace hash_bucket
{
	template<class T>
	struct HashNode
	{
		T _data;
		HashNode<T>* _next;
		HashNode(const T& data)
			:_data(data)
			,_next(nullptr)
		{}
	};
	template<class K>
	struct HashFunc
	{
		size_t operator()(const K& key)
		{
			return (size_t)key;
		}
	};
	template<> // 对string类型进行特化
	struct HashFunc<string>
	{
		size_t operator()(const string& s)
		{
			size_t hash = 0;
			for (auto e : s)
			{
				hash *= 31;
				hash += e;
			}
			return hash;
		}
	};
	template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>
	class HashTable
	{
		typedef HashNode<T> Node;
	public:
		HashTable()
		{
			_tables.resize(10, nullptr);
		}
		~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;
			}
		}
		bool Insert(const T& data)
		{
			Hash hs;
			KeyOfT kot;
			if (Find(kot(data)))
				return false;
			size_t hashi = hs(kot(data)) % _tables.size();
			// 负载因子==1,扩容
			if (_n == _tables.size())
			{
				vector<Node*> newtables(_tables.size() * 2, nullptr);
				for (size_t i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;
						// 旧表中的结点,挪动到新表重新映射的位置
						size_t hashi = hs(kot(cur->_data)) % newtables.size();
						// 头插到新表
						cur->_next = newtables[hashi];
						newtables[hashi] = cur;
						cur = next;
					}
					// 原来的桶取完后要置空
					_tables[i] = nullptr;
				}
				_tables.swap(newtables);
			}
			// 头插
			Node* newNode = new Node(data);
			newNode->_next = _tables[hashi];
			_tables[hashi] = newNode;
			++_n;
			return true;
		}
		Node* Find(const K& key)
		{
			Hash hs;
			KeyOfT kot;
			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)
		{
			Hash hs;
			KeyOfT kot;
			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;
					delete cur;
					return true;
				}
				prev = cur;
				--_n;
				cur = cur->_next;
			}
			return false;
		}
	private:
		vector<Node*> _tables;
		size_t _n = 0; // 表中存储数据个数
	};
}

 unordered_set和unordered_map类定义为

namespace cxf
{
	template<class K>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	private:
		hash_bucket::HashTable<K, K, SetKeyOfT> _ht;
	};
}
namespace cxf
{
	template<class K, class V>
	class nuordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	private:
		hash_bucket::HashTable<K, pair<K, V>, MapKeyOfT> _ht;
	};
}

2、unordered_set和unordered_map的插入

在上面,我们以及给哈希表增加了模板参数KeyOfT。这个模板参数是一个仿函数,就是为了当结点的数据类型是pair时,取出里面的first来计算,当结点数据类型是Key时,则不进行操作

unordered_set 

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

unordered_map

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

3、迭代器

3.1 operator++

unordered_set和unordered_map都是单向迭代器,所以只有operator++,没有operator--

我们先来了解一下operator++是怎么运行的。假设迭代器中是一个结点的指针,若当前桶不为空,则进入这个桶,++一次走一个结点,直到当前桶遍历完,再去下一个桶,若当前桶为空,则不会进去遍历,继续向后找不为空的桶。那遍历完一个桶后,要如何找到下一个桶的第一个结点呢?

会发现,若迭代器中只有一个结点的指针是做不到的,因为迭代器需要能够拿到哈希表。此时可以将哈希表内部的vector传过去,也可以传一个哈希表对象过去。我们这里采用的是传一个哈希表对象过去。所以迭代器的成员变量有结点的指针和哈希表对象的指针

template<class K, class T, class KeyOfT, class Hash>
struct HTIterator
{
	typedef HashNode<T> Node;
	typedef HTIterator<K, T, KeyOfT, Hash> Self;
	Node* _node; // 结点指针
	HashTable<K, T, KeyOfT, Hash>* _pht; // 哈希表对象指针
	HTIterator(Node* node, HashTable<K, T, KeyOfT, Hash>* pht)
		: _node(node)
		, _pht(pht)
	{}
	T& operator*()
	{
		return _node->_data;
	}
	T* operator->()
	{
		return &_node->_data;
	}
	bool operator!=(const Self& s)
	{
		return _node != s._node;
	}
	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])
					break;
				hashi++;
			}
			// 若哈希表遍历完也没找到不为空的桶
			if (hashi == _pht->_tables.size())
			{
				_node = nullptr; // end()
			}
			else
			{
				_node = _pht->_tables[hashi];
			}
		}
		return *this;
	}
};

迭代器类模板需要这么多个模板参数是因为成员变量有哈希表对象的指针 

哈希表中创建迭代器对象需要传一个哈希表对象,此时传this即可

typedef HTIterator<K, T, KeyOfT, Hash> Iterator;
Iterator Begin()
{
	// 寻找第一个不为空的桶的第一个结点
	for (size_t i = 0; i < _tables.size(); i++)
	{
		Node* cur = _tables[i];
		if (cur)
		{
			return Iterator(cur, this);
		}
	}
	// 若都为空则返回End
	return End();
}
Iterator End()
{
	return Iterator(nullptr, this);
}

此时会存在相互依赖的问题,因为迭代器类中有哈希表对象,哈希表类中有迭代器对象,没办法把一个放到另外一个前面,此时可以将迭代器对象放在前面,如何在迭代器对象前加一个前置声明。注意,前置声明时不能有缺省参数,因为定义时已经有缺省参数了。并且迭代器类中使用了_tables,这在哈希表类中是私有的,此时有两种方法,可以写一个GetTables,还可以将迭代器类变成哈希表类的友元。注意,将类模板变成友元不能只在类名前加friend,还需要写模板参数

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 KeyOfT, class Hash>
	struct HTIterator
	{
		typedef HashNode<T> Node;
		typedef HTIterator<K, T, KeyOfT, Hash> Self;
		Node* _node; // 结点指针
		HashTable<K, T, KeyOfT, Hash>* _pht; // 哈希表对象指针
		HTIterator(Node* node, HashTable<K, T, KeyOfT, Hash>* pht)
			: _node(node)
			, _pht(pht)
		{}
		T& operator*()
		{
			return _node->_data;
		}
		T* operator->()
		{
			return &_node->_data;
		}
		bool operator!=(const Self& s)
		{
			return _node != s._node;
		}
		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])
						break;
					hashi++;
				}
				// 若哈希表遍历完也没找到不为空的桶
				if (hashi == _pht->_tables.size())
				{
					_node = nullptr; // end()
				}
				else
				{
					_node = _pht->_tables[hashi];
				}
			}
			return *this;
		}
	};
	template<class K>
	struct HashFunc
	{
		size_t operator()(const K& key)
		{
			return (size_t)key;
		}
	};
	template<> // 对string类型进行特化
	struct HashFunc<string>
	{
		size_t operator()(const string& s)
		{
			size_t hash = 0;
			for (auto e : s)
			{
				hash *= 31;
				hash += e;
			}
			return hash;
		}
	};
	template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>
	class HashTable
	{
		typedef HashNode<T> Node;
		// 友元
		template<class K, class T, class KeyOfT, class Hash>
		friend struct HTIterator;
	public:
		typedef HTIterator<K, T, KeyOfT, Hash> Iterator;
		Iterator Begin()
		{
			// 寻找第一个不为空的桶的第一个结点
			for (size_t i = 0; i < _tables.size(); i++)
			{
				Node* cur = _tables[i];
				if (cur)
				{
					return Iterator(cur, this);
				}
			}
			// 若都为空则返回End
			return End();
		}
		Iterator End()
		{
			return Iterator(nullptr, this);
		}
		HashTable()
		{
			_tables.resize(10, nullptr);
		}
		~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;
			}
		}
		bool Insert(const T& data)
		{
			Hash hs;
			KeyOfT kot;
			if (Find(kot(data)))
				return false;
			size_t hashi = hs(kot(data)) % _tables.size();
			// 负载因子==1,扩容
			if (_n == _tables.size())
			{
				vector<Node*> newtables(_tables.size() * 2, nullptr);
				for (size_t i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;
						// 旧表中的结点,挪动到新表重新映射的位置
						size_t hashi = hs(kot(cur->_data)) % newtables.size();
						// 头插到新表
						cur->_next = newtables[hashi];
						newtables[hashi] = cur;
						cur = next;
					}
					// 原来的桶取完后要置空
					_tables[i] = nullptr;
				}
				_tables.swap(newtables);
			}
			// 头插
			Node* newNode = new Node(data);
			newNode->_next = _tables[hashi];
			_tables[hashi] = newNode;
			++_n;
			return true;
		}
		Node* Find(const K& key)
		{
			Hash hs;
			KeyOfT kot;
			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)
		{
			Hash hs;
			KeyOfT kot;
			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;
					delete cur;
					return true;
				}
				prev = cur;
				--_n;
				cur = cur->_next;
			}
			return false;
		}
	private:
		vector<Node*> _tables;
		size_t _n = 0; // 表中存储数据个数
	};
}

3.2 const迭代器

哈希表的const迭代器

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 Ptr, class Ref, class KeyOfT, class Hash>
	struct HTIterator
	{
		typedef HashNode<T> Node;
		typedef HTIterator<K, T, Ptr, Ref, KeyOfT, Hash> Self;
		Node* _node; // 结点指针
		HashTable<K, T, KeyOfT, Hash>* _pht; // 哈希表对象指针
		HTIterator(Node* node, HashTable<K, T, KeyOfT, Hash>* pht)
			: _node(node)
			, _pht(pht)
		{}
		Ref operator*()
		{
			return _node->_data;
		}
		Ptr operator->()
		{
			return &_node->_data;
		}
		bool operator!=(const Self& s)
		{
			return _node != s._node;
		}
		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])
						break;
					hashi++;
				}
				// 若哈希表遍历完也没找到不为空的桶
				if (hashi == _pht->_tables.size())
				{
					_node = nullptr; // end()
				}
				else
				{
					_node = _pht->_tables[hashi];
				}
			}
			return *this;
		}
	};
	template<class K>
	struct HashFunc
	{
		size_t operator()(const K& key)
		{
			return (size_t)key;
		}
	};
	template<> // 对string类型进行特化
	struct HashFunc<string>
	{
		size_t operator()(const string& s)
		{
			size_t hash = 0;
			for (auto e : s)
			{
				hash *= 31;
				hash += e;
			}
			return hash;
		}
	};
	template<class K, class T, class KeyOfT, class Hash = HashFunc<K>>
	class HashTable
	{
		typedef HashNode<T> Node;
		// 友元
		template<class K, class T, class Ptr, class Ref, class KeyOfT, class Hash>
		friend struct HTIterator;
	public:
		typedef HTIterator<K, T, T*, T&, KeyOfT, Hash> Iterator;
		typedef HTIterator<K, T, const T*, const T&, KeyOfT, Hash> ConstIterator;
		Iterator Begin()
		{
			// 寻找第一个不为空的桶的第一个结点
			for (size_t i = 0; i < _tables.size(); i++)
			{
				Node* cur = _tables[i];
				if (cur)
				{
					return Iterator(cur, this);
				}
			}
			// 若都为空则返回End
			return End();
		}
		Iterator End()
		{
			return Iterator(nullptr, this);
		}
		ConstIterator Begin() const
		{
			// 寻找第一个不为空的桶的第一个结点
			for (size_t i = 0; i < _tables.size(); i++)
			{
				Node* cur = _tables[i];
				if (cur)
				{
					return ConstIterator(cur, this);
				}
			}
			// 若都为空则返回End
			return End();
		}
		ConstIterator End() const
		{
			return ConstIterator(nullptr, this);
		}
		HashTable()
		{
			_tables.resize(10, nullptr);
		}
		~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;
			}
		}
		bool Insert(const T& data)
		{
			Hash hs;
			KeyOfT kot;
			if (Find(kot(data)))
				return false;
			size_t hashi = hs(kot(data)) % _tables.size();
			// 负载因子==1,扩容
			if (_n == _tables.size())
			{
				vector<Node*> newtables(_tables.size() * 2, nullptr);
				for (size_t i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;
						// 旧表中的结点,挪动到新表重新映射的位置
						size_t hashi = hs(kot(cur->_data)) % newtables.size();
						// 头插到新表
						cur->_next = newtables[hashi];
						newtables[hashi] = cur;
						cur = next;
					}
					// 原来的桶取完后要置空
					_tables[i] = nullptr;
				}
				_tables.swap(newtables);
			}
			// 头插
			Node* newNode = new Node(data);
			newNode->_next = _tables[hashi];
			_tables[hashi] = newNode;
			++_n;
			return true;
		}
		Node* Find(const K& key)
		{
			Hash hs;
			KeyOfT kot;
			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)
		{
			Hash hs;
			KeyOfT kot;
			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;
					delete cur;
					return true;
				}
				prev = cur;
				--_n;
				cur = cur->_next;
			}
			return false;
		}
	private:
		vector<Node*> _tables;
		size_t _n = 0; // 表中存储数据个数
	};
}

接下来实现Unordered_set和unordered_map的迭代器,这里与set和map是类似的,需要将参数修改为const,防止通过普通迭代器修改其值

namespace cxf
{
	template<class K>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT>::Iterator iterator; 
		typedef typename hash_bucket::HashTable<K, const 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, const K, SetKeyOfT> _ht;
	};
}
namespace cxf
{
	template<class K, class V>
	class nuordered_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>::Iterator iterator;
		typedef typename hash_bucket::HashTable<K, pair<const 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<const K, V>, MapKeyOfT> _ht;
	};
}

此时使用const迭代器时,会报错,因为我们迭代器里面的哈希表对象的指针是普通类型的,而当const对象调用Begin或End时,传过去的this是const的,属于权限放大,会报错,所以,我们需要将迭代器中的哈希表对象的指针修改为const的

Node* _node; // 结点指针
const HashTable<K, T, KeyOfT, Hash>* _pht; // 哈希表对象指针
HTIterator(Node* node, const HashTable<K, T, KeyOfT, Hash>* pht)
	: _node(node)
	, _pht(pht)
{}

 4、find

修改哈希表的Find,使之返回迭代器

Iterator Find(const K& key)
{
	Hash hs;
	KeyOfT kot;
	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);
}

还可以使用Find修改I女色让他,使容器内不会冗余

bool Insert(const T& data)
{
	Hash hs;
	KeyOfT kot;
	if (Find(kot(data)) != End())
		return false;
	size_t hashi = hs(kot(data)) % _tables.size();
	// 负载因子==1,扩容
	if (_n == _tables.size())
	{
		vector<Node*> newtables(_tables.size() * 2, nullptr);
		for (size_t i = 0; i < _tables.size(); i++)
		{
			Node* cur = _tables[i];
			while (cur)
			{
				Node* next = cur->_next;
				// 旧表中的结点,挪动到新表重新映射的位置
				size_t hashi = hs(kot(cur->_data)) % newtables.size();
				// 头插到新表
				cur->_next = newtables[hashi];
				newtables[hashi] = cur;
				cur = next;
			}
			// 原来的桶取完后要置空
			_tables[i] = nullptr;
		}
		_tables.swap(newtables);
	}
	// 头插
	Node* newNode = new Node(data);
	newNode->_next = _tables[hashi];
	_tables[hashi] = newNode;
	++_n;
	return true;
}

5、unordered_map的operator[]

首先,需要修改Insert,使之返回值为pair

pair<Iterator, bool> Insert(const T& data)
{
	Hash hs;
	KeyOfT kot;
	Iterator it = Find(kot(data));
	if (it != End())
		return make_pair(it, false);
	size_t hashi = hs(kot(data)) % _tables.size();
	// 负载因子==1,扩容
	if (_n == _tables.size())
	{
		vector<Node*> newtables(_tables.size() * 2, nullptr);
		for (size_t i = 0; i < _tables.size(); i++)
		{
			Node* cur = _tables[i];
			while (cur)
			{
				Node* next = cur->_next;
				// 旧表中的结点,挪动到新表重新映射的位置
				size_t hashi = hs(kot(cur->_data)) % newtables.size();
				// 头插到新表
				cur->_next = newtables[hashi];
				newtables[hashi] = cur;
				cur = next;
			}
			// 原来的桶取完后要置空
			_tables[i] = nullptr;
		}
		_tables.swap(newtables);
	}
	// 头插
	Node* newNode = new Node(data);
	newNode->_next = _tables[hashi];
	_tables[hashi] = newNode;
	++_n;
	return make_pair(Iterator(newNode, this), true);
}
V& operator[](const K& key)
{
	pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));
	return ret.first->second;
}

6、对于无法取模的类型

在哈希表中,对于不可取模的类型我们是使用仿函数来将不可取模的类型转为整型,使之能够取模,但是,我们控制的仿函数都在底层的哈希表,unordered_set和unordered_map并不能控制,这是不行的,所以我们需要将缺省值放到unordered_set和unordered_map的类模板中,而不是底层的哈希表

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 Ptr, class Ref, class KeyOfT, class Hash>
	struct HTIterator
	{
		typedef HashNode<T> Node;
		typedef HTIterator<K, T, Ptr, Ref, KeyOfT, Hash> Self;
		Node* _node; // 结点指针
		const HashTable<K, T, KeyOfT, Hash>* _pht; // 哈希表对象指针
		HTIterator(Node* node, const HashTable<K, T, KeyOfT, Hash>* pht)
			: _node(node)
			, _pht(pht)
		{}
		Ref operator*()
		{
			return _node->_data;
		}
		Ptr operator->()
		{
			return &_node->_data;
		}
		bool operator!=(const Self& s)
		{
			return _node != s._node;
		}
		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])
						break;
					hashi++;
				}
				// 若哈希表遍历完也没找到不为空的桶
				if (hashi == _pht->_tables.size())
				{
					_node = nullptr; // end()
				}
				else
				{
					_node = _pht->_tables[hashi];
				}
			}
			return *this;
		}
	};
	template<class K>
	struct HashFunc
	{
		size_t operator()(const K& key)
		{
			return (size_t)key;
		}
	};
	template<> // 对string类型进行特化
	struct HashFunc<string>
	{
		size_t operator()(const string& s)
		{
			size_t hash = 0;
			for (auto e : s)
			{
				hash *= 31;
				hash += e;
			}
			return hash;
		}
	};
	template<class K, class T, class KeyOfT, class Hash>
	class HashTable
	{
		typedef HashNode<T> Node;
		// 友元
		template<class K, class T, class Ptr, class Ref, class KeyOfT, class Hash>
		friend struct HTIterator;
	public:
		typedef HTIterator<K, T, T*, T&, KeyOfT, Hash> Iterator;
		typedef HTIterator<K, T, const T*, const T&, KeyOfT, Hash> ConstIterator;
		Iterator Begin()
		{
			// 寻找第一个不为空的桶的第一个结点
			for (size_t i = 0; i < _tables.size(); i++)
			{
				Node* cur = _tables[i];
				if (cur)
				{
					return Iterator(cur, this);
				}
			}
			// 若都为空则返回End
			return End();
		}
		Iterator End()
		{
			return Iterator(nullptr, this);
		}
		ConstIterator Begin() const
		{
			// 寻找第一个不为空的桶的第一个结点
			for (size_t i = 0; i < _tables.size(); i++)
			{
				Node* cur = _tables[i];
				if (cur)
				{
					return ConstIterator(cur, this);
				}
			}
			// 若都为空则返回End
			return End();
		}
		ConstIterator End() const
		{
			return ConstIterator(nullptr, this);
		}
		HashTable()
		{
			_tables.resize(10, nullptr);
		}
		~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)
		{
			Hash hs;
			KeyOfT kot;
			Iterator it = Find(kot(data));
			if (it != End())
				return make_pair(it, false);
			size_t hashi = hs(kot(data)) % _tables.size();
			// 负载因子==1,扩容
			if (_n == _tables.size())
			{
				vector<Node*> newtables(_tables.size() * 2, nullptr);
				for (size_t i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;
						// 旧表中的结点,挪动到新表重新映射的位置
						size_t hashi = hs(kot(cur->_data)) % newtables.size();
						// 头插到新表
						cur->_next = newtables[hashi];
						newtables[hashi] = cur;
						cur = next;
					}
					// 原来的桶取完后要置空
					_tables[i] = nullptr;
				}
				_tables.swap(newtables);
			}
			// 头插
			Node* newNode = new Node(data);
			newNode->_next = _tables[hashi];
			_tables[hashi] = newNode;
			++_n;
			return make_pair(Iterator(newNode, this), true);
		}
		Iterator Find(const K& key)
		{
			Hash hs;
			KeyOfT kot;
			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)
		{
			Hash hs;
			KeyOfT kot;
			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;
					delete cur;
					return true;
				}
				prev = cur;
				--_n;
				cur = cur->_next;
			}
			return false;
		}
	private:
		vector<Node*> _tables;
		size_t _n = 0; // 表中存储数据个数
	};
}
namespace cxf
{
	template<class K, class Hash = hash_bucket::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;
	};
}
namespace cxf
{
	template<class K, class V, class Hash = hash_bucket::HashFunc<K>>
	class nuordered_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 = _ht.Insert(make_pair(key, V()));
			return ret.first->second;
		}
	private:
		hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;
	};
}

此时可以写一个自定义类型来验证,注意这个自定义类型一定要重载operator==,并且operator==最好写为const的

struct Date
{
	int _year;
	int _month;
	int _day;
	bool operator==(const Date & d) const
	{
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}
};
struct HashDate
{
	size_t operator()(const Date& key)
	{
		return (key._year * 31 + key._month) * 31 + key._day;
	}
};
void test_unordered_set()
{
	cxf::unordered_set<Date, HashDate> us;
	us.insert({ 2024,8,3 });
	us.insert({ 2024,8,4 });
}

7、介绍unordered_set的几个函数

unordered_map与unordered_set是一样的

7.1 bucket_count

返回unordered_set中桶的数量(包括空桶)

7.2 bucket_size

返回unordered_set第n个桶的数据个数

7.3 bucket

 

返回这个值在哪一个桶

7.4 reserve

 

提前开辟好空间,可以避免扩容时的时间损耗 

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

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

相关文章

Gcc/G++编译C/C++文件(主要以C++语言为主,C语言就做阐述 用法一样 就是将G++换成GCC)

首先&#xff0c;我们在Linux中创建一个helloc.cc文件(C文件) vim helloc.cc 直接用g裸编译 g helloc.cc 生成的a.out就是二进制可执行文件 如果要产生 自定义可执行文件 就需要下面的编译步骤 繁琐操作 g -c helloc.cc 会生成目标文件 g -o hello helloc.o 此时hell…

仿SOUL社交友附近人婚恋约仿陌陌APP系统源码

专门为单身男女打造的恋爱交友社区&#xff0c;就是一个由千千万万单身男女组建的大家庭。他们来自全国各地&#xff0c;或许有着不同的人生经历&#xff0c;却有着共同的对恋爱交友的渴望。他们可以通过文字、语音、视频聊天的方式&#xff0c;和镜头前的彼此诉说自己工作中发…

95页PPT丨IBM-IT应用规划

一、IBM针对IT应用规划项目核心内容IBM在IT应用规划项目中的核心内容&#xff0c;旨在帮助企业实现数字化转型&#xff0c;优化IT资源配置&#xff0c;并确保IT战略与业务目标的一致性。以下是IBM IT应用规划项目的详细核心内容&#xff1a; 资料下载方式&#xff0c;请看每张…

LabVIEW与CANopen实现自动化生产线的设备控制与数据采集

在某工厂的自动化生产线上&#xff0c;多个设备通过CANopen网络进行通信和控制。这些设备包括传感器、执行器和PLC&#xff0c;它们共同负责监测和控制生产过程中的关键参数&#xff0c;如温度、压力、速度等。为了实现对整个生产线的集中监控和管理&#xff0c;工厂决定使用La…

深入理解同城代驾系统源码:技术架构与实现细节

今天&#xff0c;小编将深入讲解同城代驾系统的技术架构与实现细节。 一、同城代驾系统的基本功能模块 一个完整的同城代驾系统通常包括以下核心功能模块&#xff1a; 1.用户端应用 2.司机端应用 3.后台管理系统 4.消息推送与通知 二、技术架构设计 同城代驾系统的技术架…

程序设计基础(c语言)_补充_1

1、编程应用双层循环输出九九乘法表 #include <stdio.h> #include <stdlib.h> int main() {int i,j;for(i1;i<9;i){for(j1;j<i;j)if(ji)printf("%d*%d%d",j,i,j*i);elseprintf("%d*%d%-2d ",j,i,j*i);printf("\n");}return 0…

DNS处理模块 dnspython

DNS处理模块 dnspython 标题介绍安装dnspython 模块常用方法介绍实践&#xff1a;DNS域名轮询业务监控 标题介绍 Dnspython 是 Python 的 DNS 工具包。它可用于查询、区域传输、动态更新、名称服务器测试和许多其他事情。 dnspython 模块提供了大量的 DNS 处理方法&#xff0c…

Flink 实时数仓(五)【DWD 层搭建(三)交易域事实表】

前言 今天开始交易域事实表的创建&#xff0c;上一节流量域中的表&#xff08;其实就是一个 kafka 主题&#xff09;数据来自于日志&#xff0c;而交易域的数据来自于业务系统&#xff0c;业务表之间是有关联性的。 我们之前在离线数仓中&#xff08;声明粒度&#xff08;最细粒…

oracle数据库监控数据库中某个表是否正常生成

事情经过&#xff1a; 公司某业务系统每月25日0点会自动生成下个月的表&#xff0c;表名字是tabname_202407的格式。由于7月25日0点做系统保养的时候重启了应用系统服务&#xff0c;导致8月份的表没有生成。最终操作业务影响&#xff0c;为此决定对这个表进行监控&#xff0c;…

Depth Anything——强大的单目深度估计模型

概述 单目深度估计&#xff08;Monocular Depth Estimation, MDE&#xff09;是一项在计算机视觉领域中非常重要的技术&#xff0c;它旨在从单张图像中恢复出场景的三维结构。这项技术对于机器人导航、自动驾驶汽车、增强现实&#xff08;AR&#xff09;和虚拟现实&#xff08…

DVWA(SQL注入)medium、high

medium &#xff08;1&#xff09;判断注入是字符型还是数值型 数值型&#xff0c;获得了用户信息。 id 1 or 11 &#xff08;2&#xff09;查询字段数 为3时报错&#xff0c;代表字段数为2。 1 order by 3 &#xff08;3&#xff09;显示字段顺序 1 union select 1,2 &…

大数据与人工智能:数据隐私与安全的挑战_ai 和 数据隐私

前言 1.背景介绍 随着人工智能(AI)和大数据技术的不断发展&#xff0c;我们的生活、工作和社会都在不断变化。这些技术为我们提供了许多好处&#xff0c;但同时也带来了一系列挑战&#xff0c;其中数据隐私和安全是最为关键的之一。数据隐私和安全问题的出现&#xff0c;主要…

Redis7.x安装系列教程(一)单机部署

1、前言&环境准备说明 本文及接下来3篇将详细介绍在linux环境Redis7.X源码安装系列教程&#xff0c;从最简单的单机部署开始&#xff0c;逐步升级主从部署、哨兵部署和集群部署。 环境准备&#xff1a;如果有条件的用云服务器&#xff0c;如果没有的使用VMware 虚拟机&am…

【精通Redis】Redis事务

文章目录 前言一、标准事务1.1 标准事务的特性1.2 标准事务的生命周期1.3 事务的作用 二、Redis事务2.1 Redis事务的特性2.2 Redis事务与普通事务的区别 三、Redis事务常用命令总结 前言 我们在使用Redis的时候&#xff0c;有时为了处理多个结构&#xff0c;需要向Redis中一次…

Python数据结构实战:列表、字典与集合的高效使用

前言 在编程中&#xff0c;选择合适的数据结构对于提高程序效率至关重要。本文将介绍Python中最常用的数据结构——列表&#xff08;list&#xff09;、字典&#xff08;dict&#xff09;和集合&#xff08;set&#xff09;&#xff0c;并探讨它们的内部实现以及如何高效地使用…

The operation was rejected by your operating system. code CERT_HAS_EXPIRED报错解决

各种报错&#xff0c;试了清缓存&#xff0c;使用管理员权限打开命令行工具&#xff0c;更新npm&#xff0c;都不好使 最终解决&#xff1a;删除 c:/user/admin/ .npmrc

我的最爱之《达明一派》

达明一派&#xff0c;是我最爱。刘以达(Tats)与黄耀明(Anthony Wong)在1980年代的香港组成的二人流行音乐组合&#xff0c;在90年代&#xff0c;网络还没兴起时&#xff0c;那是卡带流行的岁月。90年代&#xff0c;我与好友&#xff0c;同考大学&#xff0c;他留在了南充读读书…

世媒讯带您了解什么是媒体邀约

什么是媒体邀约&#xff1f;其实媒体邀约是一种公关策略&#xff0c;旨在通过邀请媒体记者和编辑参加特定的活动、发布会或其他重要事件&#xff0c;以确保这些活动能够得到广泛的报道和关注。通过这种方式&#xff0c;企业和组织希望能够传达重要信息&#xff0c;提高品牌知名…

网络监控软件的作用是什么|企业用的六款网络监控软件

网络监控软件是干什么的呢&#xff1f;它是用来管理网络安全的&#xff0c;尤其是对于企业而言至关重要&#xff0c;下面我为你推荐六款知名的网络监控软件。 1. 安企神 功能特点&#xff1a; 全面监控&#xff1a;提供电脑屏幕监控、文件操作监控、聊天记录监控等功能&#…

全开源图床系统源码

一款专为个人需求设计的高效图床解决方案&#xff0c;集成了强大的图片压缩功能与优雅的前台后台管理界面。 项目结构精简高效&#xff0c;提供自定义图片压缩率与尺寸设置&#xff0c;有效降低存储与带宽成本。 支持上传JPEG、PNG、GIF格式图片并转换为WEBP格式&#xff0c;…