【C++】用哈希桶模拟实现unordered_set和unordered_map

这篇具有很好参考价值的文章主要介绍了【C++】用哈希桶模拟实现unordered_set和unordered_map。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、哈希介绍

1.1 哈希概念

顺序结构中(数组)查找一个元素需要遍历整个数组,时间复杂度为O(N);树形结构中(二叉搜索树)查找一个元素,时间复杂度最多为树的高度次logN。理想的搜索方法:可以不经过任何比较,一次直接从表中得到要搜索的元素。 构造一种存储结构,通过某种函数使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。

主要有3种操作:

  • 插入——根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放
  • 查找——根据要搜索的元素的关键码,用函数计算出存储位置,取该位置的元素关键码进行比较,如果相等,查找成功
  • 删除——根据待删除元素的关键码计算出该元素的存储位置,如果改元素存在,则进行删除

该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(Hash Table)(或者称散列表)

哈希函数设置为:hash(key) = key % capacity; capacity为存储元素底层空间总的大小。
【C++】用哈希桶模拟实现unordered_set和unordered_map,C++,哈希算法,c++,算法,开发语言,c语言,数据结构
用该方法进行搜索不必进行多次关键码的比较,因此搜索的速度比较快

如果在上面的例子中插入元素44会怎样?44%10也是4,与原来元素4的位置冲突了。那么这个新插入的44应该如何放置呢?

1.2 哈希冲突解决

首先要知道哈希冲突的原因——哈希函数设计不够合理
哈希函数设计原则:

  • 哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1之间
  • 哈希函数计算出来的地址能均匀分布在整个空间中
  • 哈希函数应该比较简单

注意:哈希函数设计的越精妙,产生哈希冲突的可能性就越低,但是无法避免哈希冲突

下面介绍两种常见的哈希函数:

  1. 闭散列
  2. 开散列

1.2.1 闭散列

闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到冲突位置中的“下一个” 空位置中去,这里的下一个可能也有元素,所以可能继续重复前面的操作,直到遇到空位置。

线性探测:
从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止。
【C++】用哈希桶模拟实现unordered_set和unordered_map,C++,哈希算法,c++,算法,开发语言,c语言,数据结构
44%4=4,与元素4的位置冲突,到它的下一个位置,位置5也有元素,继续下一个,直到位置8没有存储元素,就把44存储到位置8中。

1.2.2 开散列

开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。

【C++】用哈希桶模拟实现unordered_set和unordered_map,C++,哈希算法,c++,算法,开发语言,c语言,数据结构

开散列中每个桶中放的都是发生哈希冲突的元素。

二、哈希桶

2.1 实现哈希桶

2.1.1 构造节点和声明成员变量

哈希表的每个位置是一个桶,这个桶的结构是单链表,单链表由每个节点组成。节点有数据域和指针域,指针域是用来连接下一个节点的,数据域存放的是节点的值。节点的数据域有两种:k模型和kv模型。这里实现哈希桶的是数据域是kv模型。

template<class K, class V>
struct HashNode
{
	HashNode<K, V>* _next;
	pair<K, V> _kv;
	HashNode(const pair<K,V>& kv)
		:_kv(kv)
		, _next(nullptr)
	{}
};

成员变量有存放的数据个数和哈希表的每个位置,即每个桶,用头指针进行连接,所以哈希表的每个位置是单链表的头指针。

vector<Node*> _table;//哈希表的每个位置-桶-单链表
size_t _n = 0;//存储的元素个数

2.1.2 构造与析构

1️⃣构造
刚开始给哈希表一定的空间大小,每个位置初始化为空指针。

HashTable(size_t n = 5)
{
	_table.resize(n, nullptr);
}

2️⃣析构
哈希表的结构是STL库中的vector,当程序结束时,vector会自动调用它的析构来清理哈希表,但是表中的每个位置是单链表,单链表的每个节点是动态开辟出来的,vector的析构不能清理它们。所以要自己写析构函数来清理这些节点。

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

2.1.3 仿函数

哈希函数的计算公式:hash(key) = key % capacity,capacity就是表的空间大小,key必须是整数,但是key值是不确定的,有可能是整形、浮点型或者是字符串,所以要对key值作一些处理,使其变成整数才能进行取模操作。

1️⃣key不是字符串
返回值都转化成无符号整数

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

2️⃣key是字符串
因为传过来的参数固定就是字符串(string类型),不像前面,可能是int、double等,所以这里可以直接特化处理。定义一个临时变量为无符号整数作为返回值,遍历每个字符加到临时变量里,每个字符会自动转换成ASCII码值,然后再乘上权值131(在《The C Programming Language》书中有解释),保证不会出现year和raey相同的场景。

template<>
struct HashFind<string>
{
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (auto& e : s)
		{
			hash += e;
			hash *= 131;
		}
		return hash;
	}
};

在后面的操作中使用到哈希函数:hash(key) = key % capacity,都要通过调用仿函数来实现。

2.1.4 查找

查找一个元素是否存在,首先要计算出该元素的位置。假设该元素存在,但是它在某个位置的桶中,通过遍历单链表找到该元素,然后返回它在链表中的节点位置。不存在,返回nullptr

Node* Find(const K& key)
{
	Hash hs;
	size_t hashi = hs(key) % _table.size();//表中的位置
	Node* cur = _table[hashi];//得到当前位置头节点
	while (cur)
	{
		if (cur->_kv.first == key)//找到了
		{
			return cur;
		}
		cur = cur->_next;
	}
	//cur为空,不存在这个数据
	return nullptr;
}

2.1.5 插入

  1. 插入新的元素,不能有重复的,所以先对要插入的值进行查找,如果找到了,说明是重复元素,不能插入,返回失败。
  2. 插入新的数据不是重复元素,计算该元素在哈希表的映射位置,创建一个新节点,用头插法插入。
  3. 如果要插入数据前,哈希表的元素个数与哈希表的空间大小相等,就要扩容。创建一个新的哈希表,扩容的大小可以给原来的两倍,初始化为空。遍历旧表,将旧表的节点移动到新表中。注意,移动的过程中节点在旧表中的位置与新表可能是不对应的,所以还要用哈希函数得到节点在新表的位置,然后插入的话还是头插法。旧表中的每个位置即每个桶移动完成,,就将旧表的该位置置空。最后全部转移完,把旧表和新表进行交换,后面使用的就是新表了。
bool Insert(const pair<K,V>& kv)
{
	//重复元素不能插入
	if (Find(kv.first))
	{
		return false;
	}
	Hash hs;
	//扩容
	if (_n == _table.size())
	{
		vector<Node*> newTable(2 * _table.size(), nullptr);//新的空间
		//将旧表的节点移到新表中,再交换
		for (size_t i = 0; i < _table.size(); i++)
		{
			Node* cur = _table[i];
			while (cur)
			{
				size_t hashi = hs(_table[i]->_kv.first) % newTable.size();
				Node* next = cur->_next;
				cur->_next = newTable[hashi];
				newTable[hashi] = cur;
				cur = next;
			}
			_table[i] = nullptr;
		}
		_table.swap(newTable);
	}
	
	//插入数据
	size_t hashi = hs(kv.first) % _table.size();//表中的位置
	Node* newNode = new Node(kv);//新节点
	//头插法
	newNode->_next = _table[hashi];
	_table[hashi] = newNode;
	++_n;
	return true;
}

2.1.6 删除

删除某个元素,首先得查找该元素是否存在。如果不存在返回false;存在,通过哈希函数计算该元素的位置(是哪个桶的),得到该位置的头指针(第一个节点),然后遍历单链表,找到后删除。

遍历的过程中要注意两种可能:

  1. 要删除的节点是第一个节点
  2. 要删除的节点是中间某个节点或者最后一个节点
bool Erase(const K& key)
{
	Hash hs;
	Node* ret = Find(key);
	if (ret)
	{
		size_t hashi = hs(ret->_kv.first) % _table.size();//先找到表的位置
		Node* cur = _table[hashi];//得到该位置的头节点
		Node* prev = nullptr;//前面的节点连接
		while (cur)
		{
			if (cur->_kv.first == key)
			{
				if (prev)//第一个节点不是要删除的
				{
					prev->_next = cur->_next;
				}
				else
				{
					_table[hashi] = cur->_next;
				}
				delete cur;
				break;//找到具体节点删除后跳出
			}
			prev = cur;
			cur = cur->_next;
		}
		return true;
	}
	else//找不到,删除失败
	{
		return false;
	}
}

2.2 kv模型哈希桶源代码

#include <iostream>
#include <vector>
#include <string>
using namespace std;

//仿函数
template<class K>
struct HashFind
{
	size_t operator()(const K& key)
	{
		return key;
	}
};

template<>
struct HashFind<string>
{
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (auto& e : s)
		{
			hash += e;
			hash *= 131;
		}
		return hash;
	}
};

//节点
template<class K, class V>
struct HashNode
{
	HashNode<K, V>* _next;
	pair<K, V> _kv;
	HashNode(const pair<K,V>& kv)
		:_kv(kv)
		, _next(nullptr)
	{}
};


template<class K, class V, class Hash = HashFind<K>>
class HashTable
{
	typedef HashNode<K,V> Node;
public:

	//构造
	HashTable(size_t n = 5)
	{
		_table.resize(n, nullptr);
	}

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

	//查找
	Node* Find(const K& key)
	{
		Hash hs;
		size_t hashi = hs(key) % _table.size();//表中的位置
		Node* cur = _table[hashi];//得到当前位置头节点
		while (cur)
		{
			if (cur->_kv.first == key)//找到了
			{
				return cur;
			}
			cur = cur->_next;
		}
		//cur为空,不存在这个数据
		return nullptr;
	}

	//插入
	bool Insert(const pair<K,V>& kv)
	{
		//重复元素不能插入
		if (Find(kv.first))
		{
			return false;
		}
		Hash hs;
		//扩容
		if (_n == _table.size())
		{
			vector<Node*> newTable(2 * _table.size(), nullptr);//新的空间
			//将旧表的节点移到新表中,再交换
			for (size_t i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				while (cur)
				{
					size_t hashi = hs(_table[i]->_kv.first) % newTable.size();
					Node* next = cur->_next;
					cur->_next = newTable[hashi];
					newTable[hashi] = cur;
					cur = next;
				}
				_table[i] = nullptr;
			}
			_table.swap(newTable);
		}
		
		//插入数据
		size_t hashi = hs(kv.first) % _table.size();//表中的位置
		Node* newNode = new Node(kv);//新节点
		//头插法
		newNode->_next = _table[hashi];
		_table[hashi] = newNode;
		++_n;
		return true;
	}

	//删除
	bool Erase(const K& key)
	{
		Hash hs;
		Node* ret = Find(key);
		if (ret)
		{
			size_t hashi = hs(ret->_kv.first) % _table.size();//先找到表的位置
			Node* cur = _table[hashi];//得到该位置的头节点
			Node* prev = nullptr;//前面的节点连接
			while (cur)
			{
				if (cur->_kv.first == key)
				{
					if (prev)//第一个节点不是要删除的
					{
						prev->_next = cur->_next;
					}
					else
					{
						_table[hashi] = cur->_next;
					}
					delete cur;
					break;//找到具体节点删除后跳出
				}
				prev = cur;
				cur = cur->_next;
			}
			return true;
		}
		else//找不到,删除失败
		{
			return false;
		}
	}
	
private:
	vector<Node*> _table;
	size_t _n = 0;
};

三、改造哈希桶

前面的哈希桶的数据类型是固定的kv模型,为了后面方便模拟实现unordered_set和unordered_map,将数据类型改成T,这个T可以是key,也可以是kv。到底是哪个取决于使用的是unordered_set还是unordered_map,用的是unordered_set,模板就用unordered_set的仿函数,另一个同理。

template<class T>
struct HashNode
{
	HashNode<T>* _next;
	T _data;
	HashNode(const T& data)
		:_data(data)
		, _next(nullptr)
	{}
};

Find的返回值修改为迭代器,Insert的返回值修改为pair< iterator, bool>

3.1 begin+end

1️⃣begin
遍历哈希表,一旦遇到有节点的位置,返回该位置的迭代器。迭代器需要传两个参数,该位置的头指针和当前哈希表(与后面实现迭代器类中的成员变量对应)

iterator begin()
{
	for (size_t i = 0; i < _table.size(); i++)
	{
		if (_table[i])
		{
			return iterator(_table[i], this);
		}
	}
	return end();
}

2️⃣end
返回的是最后一个节点的下一个位置,单链表的最后一个节点的下一个是空指针,所以返回迭代器,参数传的是空指针和当前哈希表。

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

3.2 迭代器

虽然哈希表的结构用的是vector,但是每个位置是单链表,++操作不能像数组一样就行,所以要对每个节点的迭代器进行封装,方便++找到下一个节点。

注意:每个位置的桶是单链表,所以没有前置- -

模板参数有KeyOfT和Hash是因为前置++需要数据类型的仿函数和转换为无符号整数的仿函数,这里先说明一下。迭代器的成员有节点应该就可以了,为什么还要哈希表类变量?因为等会前置++的代码中要通过哈希函数计算节点数据的位置,哈希函数要有哈希表的空间大小,没有哈希表类成员,怎么得到哈希表的空间大小。

其他的与之前一样,不作重复叙述了

template<class K, class T, class KeyOfT, class Hash>
struct HashIterator
{
	typedef HashNode<T> Node;
	typedef HashTable<K, T, KeyOfT, Hash> Ht;
	typedef HashIterator< K, T, KeyOfT, Hash> Self;

	Node* _node;
	Ht* _ht;

	HashIterator(Node* node, Ht* ht)
		:_node(node)
		,_ht(ht)
	{}

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

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

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

3.2.1 前置++

分为两种情况:

  1. 当前节点的下一个节点不为空
  2. 当前节点的下一个节点为空

1️⃣情况一:
【C++】用哈希桶模拟实现unordered_set和unordered_map,C++,哈希算法,c++,算法,开发语言,c语言,数据结构
直接到下一个节点即可。

2️⃣情况二:
【C++】用哈希桶模拟实现unordered_set和unordered_map,C++,哈希算法,c++,算法,开发语言,c语言,数据结构

下一个节点为空,先计算当前节点在哈希表的位置(具体哪个桶),然后在该位置往后开始找,只要遇到有节点的位置,下一个节点就是它;如果后面都没有节点,下一个节点就是空。

Self& operator++()
{
	if (_node->_next)
	{
		_node = _node->_next;
	}
	else
	{
		Hash hs;
		KeyOfT kot;
		size_t hashi = hs(kot(_node->_data)) % _ht->_table.size();
		hashi++;
		while (hashi < _ht->_table.size())
		{
			if (_ht->_table[hashi])
			{
				_node = _ht->_table[hashi];
				break;
			}
			hashi++;
		}
		if (_ht->_table.size() == hashi)
		{
			_node = nullptr;
		}
	}
	return *this;
}

3.3 改造后哈希桶与迭代器源代码

#include <iostream>
#include <vector>
#include <string>
using namespace std;

//仿函数
template<class K>
struct HashFind
{
	size_t operator()(const K& key)
	{
		return key;
	}
};

template<>
struct HashFind<string>
{
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (auto& e : s)
		{
			hash += e;
			hash *= 131;
		}
		return hash;
	}
};

//节点
template<class T>
struct HashNode
{
	HashNode<T>* _next;
	T _data;
	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 HashIterator
{
	typedef HashNode<T> Node;
	typedef HashTable<K, T, KeyOfT, Hash> Ht;
	typedef HashIterator< K, T, KeyOfT, Hash> Self;

	Node* _node;
	Ht* _ht;

	HashIterator(Node* node, Ht* ht)
		:_node(node)
		,_ht(ht)
	{}

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

	Self& operator++()
	{
		if (_node->_next)
		{
			_node = _node->_next;
		}
		else
		{
			Hash hs;
			KeyOfT kot;
			size_t hashi = hs(kot(_node->_data)) % _ht->_table.size();
			hashi++;
			while (hashi < _ht->_table.size())
			{
				if (_ht->_table[hashi])
				{
					_node = _ht->_table[hashi];
					break;
				}
				hashi++;
			}
			if (_ht->_table.size() == hashi)
			{
				_node = nullptr;
			}
		}
		return *this;
	}

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

template<class K, class T, class KeyOfT, class Hash>
class HashTable
{
	template<class K, class T, class KeyOfT, class Hash>
	friend struct HashIterator;

	typedef HashNode<T> Node;
public:
	typedef HashIterator <K, T, KeyOfT, Hash> iterator;

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

	//构造
	HashTable(size_t n = 5)
	{
		_table.resize(n, nullptr);
	}

	//析构
	~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)
	{
		Hash hs;
		KeyOfT kot;
		size_t hashi = hs(key) % _table.size();//表中的位置
		Node* cur = _table[hashi];//得到当前位置头节点
		while (cur)
		{
			if (kot(cur->_data) == key)//找到了
			{
				return iterator(cur, this);
			}
			cur = cur->_next;
		}
		//cur为空,不存在这个数据
		return end();
	}

	//插入
	pair<iterator, bool> Insert(const T& data)
	{
		KeyOfT kot;
		//重复元素不能插入
		iterator pos = Find(kot(data));
		if (pos != end())
		{
			return make_pair(pos, false);
		}
		Hash hs;
		//扩容
		if (_n == _table.size())
		{
			vector<Node*> newTable(2 * _table.size(), nullptr);//新的空间
			//将旧表的节点移到新表中,再交换
			for (size_t i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				while (cur)
				{
					size_t hashi = hs(kot(_table[i]->_data)) % newTable.size();
					Node* next = cur->_next;
					cur->_next = newTable[hashi];
					newTable[hashi] = cur;
					cur = next;
				}
				_table[i] = nullptr;
			}
			_table.swap(newTable);
		}

		//插入数据
		size_t hashi = hs(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)
	{
		Hash hs;
		KeyOfT kot;
		iterator ret = Find(key);
		if (ret != end())
		{
			size_t hashi = hs(kot(ret._node->_data)) % _table.size();//先找到表的位置
			Node* cur = _table[hashi];//得到该位置的头节点
			Node* prev = nullptr;//前面的节点连接
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev)//第一个节点不是要删除的
					{
						prev->_next = cur->_next;
					}
					else
					{
						_table[hashi] = cur->_next;
					}
					delete cur;
					break;//找到具体节点删除后跳出
				}
				prev = cur;
				cur = cur->_next;
			}
			return true;
		}
		else//找不到,删除失败
		{
			return false;
		}
	}

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

四、模拟实现unordered_set

unordered_set是k模型,它的仿函数就返回key。模板参数有key和Hash,Hash是用来转换key变成无符号整数的。其他接口调用哈希桶的即可。

#include "HashTable.h"
namespace yss
{
	template <class K, class Hash = HashFind<K>>
	class unordered_set
	{
		//仿函数
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

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

		//迭代器
		iterator begin()
		{
			return _ht.begin();
		}
		iterator end()
		{
			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:
		HashTable<K, const K, SetKeyOfT, Hash> _ht;
	};
}

【C++】用哈希桶模拟实现unordered_set和unordered_map,C++,哈希算法,c++,算法,开发语言,c语言,数据结构

typename的作用是把HashTable<K, const K, SetKeyOfT, HashFind< K >>::iterator当做一个类型,而不是变量。typedef就是重命名。

五、模拟实现unordered_map

unordered_map是kv模型,仿函数传的是kv中的key。除了查找、插入、删除外,还有operator[],可以进行统计元素等操作文章来源地址https://www.toymoban.com/news/detail-847574.html

#include "HashTable.h"
namespace yss
{
	template <class K, class V, class Hash = HashFind<K>>
	class unordered_map
	{
		//仿函数
		struct MapKeyOfT
		{
			const K& operator()(const pair<K,V>& kv)
			{
				return kv.first;
			}
		};

	public:
		typedef typename HashTable<K, pair<const K, V>, MapKeyOfT, HashFind<K>>::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);
		}

		//查找
		iterator Find(const K& key)
		{
			return _ht.Find(key);
		}

		//删除
		bool Erase(const K& key)
		{
			return _ht.Erase(key);
		}

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

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

到了这里,关于【C++】用哈希桶模拟实现unordered_set和unordered_map的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++进阶--使用哈希表实现unordered_map和unordered_set的原理与实例

    本文将介绍如何使用哈希表来实现C++ STL库中的unordered_map和unordered_set容器。我们将会解释哈希表的基本原理,并给出具体的代码示例,帮助读者更好地理解和应用哈希表。 哈希原理讲解–链接入口 set和map的实现的文章,与unordered_map实现类似 unordered_set是一种集合存储的容器

    2024年04月09日
    浏览(34)
  • 【C++学习】哈希表的底层实现及其在unordered_set与unordered_map中的封装

    🎉个人名片: 🐼作者简介:一名乐于分享在学习道路上收获的大二在校生 🙈个人主页🎉:GOTXX 🐼个人WeChat:ILXOXVJE 🐼本文由GOTXX原创,首发CSDN🎉🎉🎉 🐵系列专栏:零基础学习C语言----- 数据结构的学习之路----C++的学习之路 🐓每日一句:如果没有特别幸运,那就请特

    2024年04月16日
    浏览(34)
  • C++ 哈希+unordered_map+unordered_set+位图+布隆过滤器(深度剖析)

    想象一下,你有一个巨大的图书馆,里面摆满了成千上万本书。如果你想要找到其中一本特定的书,你会怎么做?如果你按照书的编号来有序地排列它们,找到特定的书本可能会很容易。但是,如果书籍是随机地摆放,你可能需要逐本地查找,这个过程会非常耗时。 而哈希函

    2024年02月21日
    浏览(34)
  • C++利用开散列哈希表封装unordered_set,unordered_map

    1.之前我们已经实现了开散列的哈希表,今天我们来用它封装unordered_set,unordered_map 2.本文的封装比利用红黑树封装set和map更加复杂 建议大家先去看我的红黑树封装set和map再来看本文 因为有很多地方跟红黑树封装set和map时是同样的思路和方法,所以本文不会太去赘述一遍 因为un

    2024年03月24日
    浏览(38)
  • 【C++入门到精通】哈希 (STL) _ unordered_map _ unordered_set [ C++入门 ]

    欢迎各位大佬们的关顾,本文将介绍unordered系列容器以及其中的两个重要成员: unordered_map 和 unordered_set 。unordered_map是一种无序的关联容器,它使用哈希表来存储键值对,并提供高效的插入、查找和删除操作。在本文中,我们将首先介绍unordered_map的基本概念和特点,然后详

    2024年02月08日
    浏览(30)
  • 【C++】如何用一个哈希表同时封装出unordered_set与unordered_map

    👀 樊梓慕: 个人主页  🎥 个人专栏: 《C语言》 《数据结构》 《蓝桥杯试题》 《LeetCode刷题笔记》 《实训项目》 《C++》 《Linux》 《算法》 🌝 每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.哈希桶源码  2.哈希表模板参数的控制 3.字符串作为键值如何映射哈希值

    2024年03月26日
    浏览(35)
  • 【C++】STL——用一个哈希表封装出unordered_map和unordered_set

    根据先前对unordered_map和unordered_set的学习,我们得知其底层是借助哈希表来实现的,接下来我们会使用上篇博客实现的开散列哈希表来模拟实现unordered_map和unordered_set,哈希表源代码链接: Hash/Hash/HashBucket.h · wei/cplusplus - 码云 - 开源中国 (gitee.com) 下面我们对哈希表进行改造,

    2023年04月18日
    浏览(35)
  • 从哈希桶角度看 unordered_map 与 unordered_set 的实现

    哈希函数与哈希桶是计算机科学中用于实现高效数据检索的重要工具。在之前的博客中,我们已经详细探讨了哈希的基本概念、哈希函数的构造方法以及哈希桶的工作原理等内容。在本篇博客中,我们将进一步深入探索C++中的unordered系列数据结构,并利用之前介绍的哈希桶原

    2024年03月22日
    浏览(28)
  • 改造哈希表,封装unordered_map和unordered_set

    正文开始前给大家推荐个网站,前些天发现了一个巨牛的 人工智能 学习网站, 通俗易懂,风趣幽默 ,忍不住分享一下给大家。点击跳转到网站。 unordered_map是存的是pair是K,V型的,而unordered_set是K型的,里面只存一个值,那我们如何利用一个数据结构将他们都封装出来呢?

    2024年02月03日
    浏览(34)
  • C++STL详解(十) -- 使用哈希表封装unordered_set和unordered_map

    因为不同容器的类型不同,如果是unordered_map,V代表一个键值对,如果unordered_set,V代表Key值,而底层哈希表并不知道上层容器所要传递的V模板参数类型,为了与哈希表的模板参数区分,我们将哈希表中的V模板参数改为T. 例如: 在哈希表中当我们使用Find函数进行查找时: 如果上层传递的

    2024年02月01日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包