[C++]--STL uses hash bucket simulation to implement unordered_set and unordered_map

Table of contents

1. Modification of hash bucket node

2. Hash table

1.Construction

2.Copy construction

3. Assignment operator overloading

4. Destruction

5.Iterator

6.Find

7.Insert

8.Delete

 3. Iterator

1.operator++( )

2.operator*( )

3.operator->( )

4.operator !=( )

5.operator ==( )

4. Encapsulate unordered_set and unordered_map

 1. Encapsulate unordered_set

(1) Functor SetKeyOfT

(2) Iterator

(3) Example

2. Encapsulate unordered_map

(1) Functor MapKeyOfT

(2) Iterator

(3) Example

5. Complete code snippet


1. Modification of hash bucket node

        When using hash bucket encapsulation to implement unordered_set and unordered_map, we must consider that the data elements they pass to the hash system are different. Unordered_set passes k to the hash bucket, and unordered_map passes pair to the hash bucket. Then the hash bucket faces How can these two different types of data be processed uniformly?

         When unordered_set passes k to the hash bucket, and unordered_map passes pair to the hash bucket, K and V are packaged into T, and T is used instead of pair<K, V>:

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

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

2. Hash table

The class template needs to be modified, and K must be included in the template, because K is used to calculate the position of the data mapping. Since the node type of the hash bucket is changed to T, use T instead of V. The KeyOfT functor determines whether the uploaded unordered_set or unordered_map is uploaded.

    template<class K, class T, class KeyOfT, class HashFunc = Hash<K>>
	class HashTable
	{
		typedef HashNode<T> Node;
        
        //哈希桶迭代器
		template<class K,class T,class KeyOfT,class HashFunc>
		friend struct __HTIterator;
	public:
		typedef __HTIterator<K, T, KeyOfT, HashFunc> iterator;

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

1.Construction

Just use the default constructor. The vector custom type will call its own default constructor. Size_t is a built-in type and the compiler does not process it:

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

2.Copy construction

Just assign _n directly. To copy _table, you need to traverse ht's _table, and insert each node of ht's _table into the _table table:

        //拷贝构造
		HashTable(const HashTable& ht)
		{
			_n = ht._n;//存储有效数据的个数一致
			_table.resize(ht._table.size());//开同样大小的空间

			//遍历ht,将ht的_table的每个结点都拷贝到_table中
			for (size_t i = 0; i < ht._table.size(); i++)
			{
				Node* cur = ht._table[i];
				while (cur)
				{
					Node* copy = new Node(cur->_data);
		
					//头插到新表
					copy->_next = _table[i];//copy的下一个桶为_table[i]
					_table[i] = copy;//把copy作为当前位置的第一个桶
					cur = cur->_next;//cur往下移					
				}
			}
		
		}

3. Assignment operator overloading

Just swap _table and _n:

		//赋值运算符重载
		HashTable& operator=(HashTable ht)
		{
			_table.swap(ht._table);
			swap(_n, ht._n);

			return *this;
		}

4. Destruction

Just delete each node of _table and make it empty:

		//析构
		~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;
			}
		}

5.Iterator

The parameters of the iterator include the node position and the hash table address. In the next section of iterators, we will talk about why pointers are used:

		//迭代器开始
        iterator begin()
		{
			size_t i = 0;
			while (i < _table.size())
			{
				if (_table[i])
				{
					return iterator(_table[i], this);
				}
				++i;
			}
	
			return end();
		}
		
        //迭代器结束
		iterator end()
		{
			return iterator(nullptr, this);
		}

6.Find

 At this time, the functor KeyOfT will be used. The object kot of the functor KeyOfT will take k for unordered_set, and for unordered_map, it will take the first of kv as the pair as k and key for comparison:

		//查找
		iterator Find(const K& key)
		{
			//哈希表为空
			if (_table.size() == 0)
			{
				return end();
			}
		
			KeyOfT kot;
			HashFunc hf;
			size_t index = hf(key) % _table.size();//计算在哈希表中的位置
			
			//在哈希表当前位置的所有桶中找key
			Node* cur = _table[index];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(cur,this);
				}
				else
				{
					cur = cur->_next;
				}
			}
		
			return end();
		}

7.Insert

① You need to first determine whether the data is in the hash bucket, and then directly return to the found location.

② If it is not there, you need to determine whether the hash bucket needs to be increased. If it does not need to be increased, calculate the mapping position and insert it into the hash table.

③If you need to increase the capacity, you need to take the nodes in the old table and insert them into the new table one by one, and exchange the old table and the new table.

		//插入
		pair<iterator,bool> Insert(const T& data)
		{
			KeyOfT kot;
			auto ret = Find(kot(data));
			if (ret != end())
			{
				return make_pair(ret,false);
			}
		
			//仿函数
			HashFunc hf;
		
			//负载因子为1时,进行增容
			if (_n == _table.size())
			{
				vector<Node*> newTable;
				newTable.resize(GetNextPrime(_table.size()));
		
				//取旧表中的结点,重新计算映射到新表中的位置,挂到新表中
				for (size_t i = 0; i < _table.size(); i++)
				{
					if (_table[i])
					{
						Node* cur = _table[i];
						while (cur)
						{
							Node* next = cur->_next;//保存下一个结点
							size_t index = hf(kot(cur->_data)) % newTable.size();//计算结映射到新表中的位置
		
							//头插
							cur->_next = newTable[index];//=nullptr,将cur->_next置空
							newTable[index] = cur;//将结点插入到新表
							cur = next;//哈希桶往下挪一个
						}
						_table[i] = nullptr;//当前哈希桶的第一个位置置空
					}
				}
				_table.swap(newTable);
			}
		
			//不需要增容时,头插
			size_t index = hf(kot(data)) % _table.size();
			Node* newNode = new Node(data);
		
			newNode->_next = _table[index];//让新节点newNode的next指向第一个桶
			_table[index] = newNode;//让新节点newNode做第一个桶
			++_n;//更新哈希表大小	

			return make_pair(iterator(newNode, this), true);
		}

8.Delete

① Before deleting a node, you must keep the previous node of the node. Otherwise, after deleting the changed node, the previous node will point to the next one, but the previous node will not be found.

②After finding the mapping position of the key, it is necessary to determine whether the found node is the first bucket of the current position. If so, let the current position point to the next node; if not, let the previous node point to the next node.

		//删除
		bool Erase(const K& key)
		{
			size_t index = hf(key) % _table.size();
			Node* prev = nullptr;
			Node* cur = _table[index];
		
			while (cur)
			{
				if (kot(cur->data) == key)//cur这个桶就是key
				{
					if (_table[index] == cur)//cur是第一个桶
					{
						_table[index] = cur->_next;
					}
					else//cur不是第一个桶
					{
						prev->_next = cur->_next;
					}
		
					--_n;//更新表大小
					delete cur;//删除节点
					return true;
				}
		
				prev = cur;
				cur = cur->_next;
			}
		
			return false;
		}

 3. Iterator

        The iterator needs to declare HashTable in advance, because the __HTIterator iterator is used in the HashTable class, and the pointer of the HashTable class is used in __HTIterator. Why use a pointer?
        Because when the C++ compiler compiles the source file from top to bottom, for the definition of each data, it needs to know the size of the defined data type. After predeclaring the statement class HashTable;, the compiler already knows that HashTable is a class, but the data in it is unknown, so the size of the HashTable type is also unknown, which causes compilation failure; change _node in __HTIterator After changing to the HashTable pointer type, since the pointer occupies a certain space on a specific platform (4 bytes on the Win32 platform and 8 bytes on the 64-bit platform), it can be compiled.
       

The parameters of the iterator include hash nodes and hash tables:

	// 前置声明
	template<class K, class T, class KeyOfT, class HashFunc>
	class HashTable;

	// 迭代器
	template<class K, class T, class KeyOfT, class HashFunc = Hash<K>>
	struct __HTIterator
	{
		typedef HashNode<T> Node;//哈希节点
		typedef __HTIterator<K, T, KeyOfT, HashFunc> Self;//实现++、--
		typedef HashTable<K, T, KeyOfT, HashFunc> HT;//哈希表
		Node* _node;
		HT* _pht;

		__HTIterator(Node* node, HT* pht)
			:_node(node)//哈希节点
			, _pht(pht)//哈希表
		{}
    };

1.operator++( )

As shown below, operator++ is divided into two situations:

①The current node is not the last node at the current position of the hash table, such as 2, 53, and the next node of the current node is returned.

②The current node is the last node at the current position of the hash table, such as 852, 63, and the first node at the next non-empty position needs to be returned.

 

		Self& operator++()
		{
			//不是当前位置最后一个节点
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else//是当前位置最后一个节点
			{
				KeyOfT kot;
				HashFunc hf;

				size_t index = hf(kot(_node->_data)) % _pht->_table.size();

				//找哈希表中下一个不为空的位置
				++index;
				while (index < _pht->_table.size())
				{
					if (_pht->_table[index])//不为空
					{
						_node = _pht->_table[index];
						return *this;
					}
					else//为空
					{
						++index;
					}
				}
				_node = nullptr;//后面的所有位置都为空,_node置空,返回当前位置即可
			}

			return *this;
		}

2.operator*( )

Just take _data: 

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

3.operator->( )

 Just get the address of _data:

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

4.operator !=( )

Compare s's _node and _node:

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

5.operator ==( )

Compare s's _node and _node:

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

4. Encapsulate unordered_set and unordered_map

 1. Encapsulate unordered_set

The members of unordered_set are hash tables, but template parameters need to be passed. SetKeyOfT is the functor passed to the hash table.

#pragma once
#include "HashTable.h"

namespace delia
{
	template<class K>
	class unordered_set
	{

	private:
		OpenHash::HashTable<K, K, SetKeyOfT> _ht ;

	};
}

(1) Functor SetKeyOfT

 set takes k directly:

		//set就取k
		struct SetKeyOfT
		{
			const K& operator()(const K& k)
			{
				return k;
			}
		};

(2) Iterator

        For the iterator of unordered_set, if typename is not added, the type of iterator cannot be found in the class template, because OpenHash::HashTable<K, K, SetKeyOfT>::iterator is not instantiated, so the compiler does not know that it is The type is still a member function or member variable, and compilation cannot pass.
        Use typename to indicate that OpenHash::HashTable<K, K, SetKeyOfT>::iterator is a type, not a member function or member variable, and does not need to wait until instantiation to determine it.

        The iterators of unordered_set all call the iterators of the hash table to operate:

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

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

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

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

(3) Example

Insert some data into unordered_set and print:

	void test_unordered_set1()
	{
		unordered_set<int> us;
		us.Insert(100);
		us.Insert(5);
		us.Insert(6);
		us.Insert(32);
		us.Insert(8);
		us.Insert(14);
		us.Insert(65);
		us.Insert(27);
		us.Insert(39);

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

2. Encapsulate unordered_map

The members of unordered_map are hash tables, but template parameters need to be passed. MapKeyOfT is the functor passed to the hash table  :

#pragma once
#include "HashTable.h"

namespace delia
{
	template<class K,class V>
	class unordered_map
    {

    
    private:
		OpenHash::HashTable<K, pair<K, V>, MapKeyOfT> _ht;

	};

(1) Functor MapKeyOfT

Map takes the first of kv: 

		//map就取kv的first
		struct MapKeyOfT
		{
			const K& operator()(const pair<K,V>& kv)
			{
				return kv.first;
			}
		};

(2) Iterator

  The same as the iterator definition of unordered_set, typename must be added in front, and the iterator of unordered_map all calls the iterator of the hash table to operate:

	public:
		typedef typename OpenHash::HashTable<K, pair<K,V>, MapKeyOfT>::iterator iterator;

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

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

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

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

(3) Example

	void test_unordered_map1()
	{
		unordered_map<string,string> um;
		um.Insert(make_pair("spring", "春天"));
		um.Insert(make_pair("summer", "夏天"));
		um.Insert(make_pair("autumn", "秋天"));
		um.Insert(make_pair("winter", "冬天"));

		unordered_map<string,string>::iterator it = um.begin();
		while (it != um.end())
		{
			cout << it->first << ":" << it->second << endl;
			++it;
		}
	}

5. Complete code snippet

HashTable.h

#pragma once
#include<vector>
#include<iostream>
using namespace std;

namespace OpenHash
{
	//哈希仿函数
	template<class K>
	struct Hash
	{
		size_t operator()(const K& key)
		{
			return key;
		}
	};

	//string仿函数
	template<>
	struct Hash<string>//模板特化
	{
		size_t operator()(const string& s)
		{
			size_t value = 0;
			for (auto e : s)
			{
				value += e;
				value *= 131;
			}

			return value;
		}
	};

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

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

	//前置声明HashTable,因为HashTable类中使用了__HTIterator,且__HTIterator中使用了HashTable类的指针,为什么要用指针
	//C++编译器自上而下编译源文件的时候,对每一个数据的定义,总是需要知道定义的数据类型的大小。在预先声明语句class HashTable;之后,编译器已经知道HashTable是一个类,但是其中的数据却是未知的,因此HashTable类型的大小也不知道,这样就造成了编译失败;将__HTIterator中的_node更改为HashTable指针类型之后,由于在特定的平台上,指针所占的空间是一定的(在Win32平台上是4字节,在64位平台上是8字节),这样可以通过编译
		// 前置声明
	template<class K, class T, class KeyOfT, class HashFunc>
	class HashTable;

	// 迭代器
	template<class K, class T, class KeyOfT, class HashFunc = Hash<K>>
	struct __HTIterator
	{
		typedef HashNode<T> Node;//哈希节点
		typedef __HTIterator<K, T, KeyOfT, HashFunc> Self;//实现++、--
		typedef HashTable<K, T, KeyOfT, HashFunc> HT;//哈希表
		Node* _node;
		HT* _pht;

		__HTIterator(Node* node, HT* pht)
			:_node(node)//哈希节点
			, _pht(pht)//哈希表
		{}
	
		Self& operator++()
		{
			//不是当前位置最后一个节点
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else//是当前位置最后一个节点
			{
				KeyOfT kot;
				HashFunc hf;

				size_t index = hf(kot(_node->_data)) % _pht->_table.size();

				//找哈希表中下一个不为空的位置
				++index;
				while (index < _pht->_table.size())
				{
					if (_pht->_table[index])//不为空
					{
						_node = _pht->_table[index];
						return *this;
					}
					else//为空
					{
						++index;
					}
				}
				_node = nullptr;//后面的所有位置都为空,_node置空,返回当前位置即可
			}

			return *this;
		}

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

		T* 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 HashFunc = Hash<K>>
	class HashTable
	{
		typedef HashNode<T> Node;

		template<class K,class T,class KeyOfT,class HashFunc>
		friend struct __HTIterator;
	public:
		typedef __HTIterator<K, T, KeyOfT, HashFunc> iterator;
	
		//构造函数,default指定生成默认构造函数
		HashTable() = default;

		//拷贝构造
		HashTable(const HashTable& ht)
		{
			_n = ht._n;//存储有效数据的个数一致
			_table.resize(ht._table.size());//开同样大小的空间

			//遍历ht,将ht的每个结点都拷贝到_table中
			for (size_t i = 0; i < ht._table.size(); i++)
			{
				Node* cur = ht._table[i];
				while (cur)
				{
					Node* copy = new Node(cur->_data);
		
					//头插到新表
					copy->_next = _table[i];//copy的下一个桶为_table[i]
					_table[i] = copy;//把copy作为当前位置的第一个桶
					cur = cur->_next;//cur往下移					
				}
			}
		
		}

		//赋值运算符重载
		HashTable& operator=(HashTable ht)
		{
			_table.swap(ht._table);
			swap(_n, ht._n);

			return *this;
		}

		//析构
		~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 begin()
		{
			size_t i = 0;
			while (i < _table.size())
			{
				if (_table[i])
				{
					return iterator(_table[i], this);
				}
				++i;
			}
	
			return end();
		}
		
		iterator end()
		{
			return iterator(nullptr, this);
		}
	
		size_t GetNextPrime(size_t prime)
		{
			const int PRIMECOUNT = 28;
			static const size_t primeList[PRIMECOUNT] =
			{
				53ul,97ul,193ul,389ul,769ul,
				1543ul,3079ul,6151ul,12289ul,24593ul,
				49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
				1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
				50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
				1610612741ul, 3221225473ul, 4294967291ul
			};
	
			size_t i = 0;
			for (; i < PRIMECOUNT; i++)
			{
				if (primeList[i] > prime)
				{
					return primeList[i];
				}
			}
	
			return primeList[i];
		}

		//插入
		pair<iterator,bool> Insert(const T& data)
		{
			KeyOfT kot;
			auto ret = Find(kot(data));
			if (ret != end())
			{
				return make_pair(ret,false);
			}
		
			//仿函数
			HashFunc hf;
		
			//负载因子为1时,进行增容
			if (_n == _table.size())
			{
				vector<Node*> newTable;
				newTable.resize(GetNextPrime(_table.size()));
		
				//取旧表中的结点,重新计算映射到新表中的位置,挂到新表中
				for (size_t i = 0; i < _table.size(); i++)
				{
					if (_table[i])
					{
						Node* cur = _table[i];
						while (cur)
						{
							Node* next = cur->_next;//保存下一个结点
							size_t index = hf(kot(cur->_data)) % newTable.size();//计算结映射到新表中的位置
		
							//头插
							cur->_next = newTable[index];//=nullptr,将cur->_next置空
							newTable[index] = cur;//将结点插入到新表
							cur = next;//哈希桶往下挪一个
						}
						_table[i] = nullptr;//当前哈希桶的第一个位置置空
					}
				}
				_table.swap(newTable);
			}
		
			//不需要增容时,头插
			size_t index = hf(kot(data)) % _table.size();
			Node* newNode = new Node(data);
		
			newNode->_next = _table[index];//让新节点newNode的next指向第一个桶
			_table[index] = newNode;//让新节点newNode做第一个桶
			++_n;//更新哈希表大小	

			return make_pair(iterator(newNode, this), true);
		}
		
		//查找
		iterator Find(const K& key)
		{
			//哈希表为空
			if (_table.size() == 0)
			{
				return end();
			}
		
			KeyOfT kot;
			HashFunc hf;
			size_t index = hf(key) % _table.size();//计算在哈希表中的位置
			
			//在哈希表当前位置的所有桶中找key
			Node* cur = _table[index];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(cur,this);
				}
				else
				{
					cur = cur->_next;
				}
			}
		
			return end();
		}
		
		//删除
		bool Erase(const K& key)
		{
			size_t index = hf(key) % _table.size();
			Node* prev = nullptr;
			Node* cur = _table[index];
		
			while (cur)
			{
				if (kot(cur->data) == key)//cur这个桶就是key
				{
					if (_table[index] == cur)//cur是第一个桶
					{
						_table[index] = cur->_next;
					}
					else//cur不是第一个桶
					{
						prev->_next = cur->_next;
					}
		
					--_n;//更新表大小
					delete cur;//删除节点
					return true;
				}
		
				prev = cur;
				cur = cur->_next;
			}
		
			return false;
		}

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

 UnorderedSet.h

#pragma once
#include "HashTable.h"

namespace delia
{
	template<class K>
	class unordered_set
	{
		//set就取k
		struct SetKeyOfT
		{
			const K& operator()(const K& k)
			{
				return k;
			}
		};

	public:
		//如果不加typename,类模板里面找迭代器的类型找不到,因为OpenHash::HashTable<K, K, SetKeyOfT>::iterator没有实例化,因此编译器并不知道它是类型还是成员函数或成员变量,编译无法通过
		//用typename表明OpenHash::HashTable<K, K, SetKeyOfT>::iterator是一个类型,不是成员函数或成员变量,不需要等到实例化时才能确定
		typedef typename OpenHash::HashTable<K, K, SetKeyOfT>::iterator iterator;

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

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

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

	private:
		OpenHash::HashTable<K, K, SetKeyOfT> _ht ;

	};

	void test_unordered_set1()
	{
		unordered_set<int> us;
		us.Insert(100);
		us.Insert(5);
		us.Insert(6);
		us.Insert(32);
		us.Insert(8);
		us.Insert(14);
		us.Insert(65);
		us.Insert(27);
		us.Insert(39);

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

UnorderedMap.h

#pragma once
#include "HashTable.h"

namespace delia
{
	template<class K,class V>
	class unordered_map
	{
		//map就取kv的first
		struct MapKeyOfT
		{
			const K& operator()(const pair<K,V>& kv)
			{
				return kv.first;
			}
		};

	public:
		typedef typename OpenHash::HashTable<K, pair<K,V>, MapKeyOfT>::iterator iterator;

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

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

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

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

	private:
		OpenHash::HashTable<K, pair<K, V>, MapKeyOfT> _ht;

	};

	void test_unordered_map1()
	{
		unordered_map<string,string> um;
		um.Insert(make_pair("spring", "春天"));
		um.Insert(make_pair("summer", "夏天"));
		um.Insert(make_pair("autumn", "秋天"));
		um.Insert(make_pair("winter", "冬天"));

		unordered_map<string,string>::iterator it = um.begin();
		while (it != um.end())
		{
			cout << it->first << ":" << it->second << endl;
			++it;
		}
	}
}

Test.cpp

#define  _CRT_SECURE_NO_WARNINGS  1
#include "HashTable.h"
#include "UnorderedSet.h"
#include "UnorderedMap.h"

int main()
{
	delia::test_unordered_set1();
	delia::test_unordered_map1();

	return 0;
}

operation result:

unordered_set:

Since the initial size of the hash table is 53, the first element in primeList, when inserting:

100%53=47,

5%53=5,

6%53=6,

8%53=8,

65%53=12,

14%53=14,

27%53=27,

32%53=32,

39%53=39

There is no conflict, so the order is 5, 6, 8, 65, 14, 27, 32, 39, 100.

 

unordered_map:

The Hash algorithm of the string sums each character *131 and then modulo 53:

The ASCII comparison table is as follows:

97 a
98 b
99 c
100 d
101 e
102 f
103 g
104 h
105 i
106 j
107 k
108 l
109 m
110 n
111 o
112 p
113 q
114 r
115 s
116 t
117 u
118 v
119 w
120 x
121 y
122 z

spring:(((((((115*131)+112)*131)+114)*131+105)*131+110)*131+103)*131=359074819, an overflow occurred in the middle, so the result is obtained 359074819, then use 359074819 to modulo 53. Summer, autumn and winter are also calculated in this way.


 

Guess you like

Origin blog.csdn.net/gx714433461/article/details/126963942