<C++> 哈希表的完善及unordered_封装

目录

一、哈希表的完善

1.优化:哈希函数

2.优化:素数大小

3.新增:迭代器类

二、封装实现 unordered_set 和 unordered_map

1.解决 k/v 参数冲突问题

2.解决 key 的获取问题

3.解决 unordered_set 迭代器非法操作

4.调整函数返回值

5.unordered_map 新增 operator[ ]

三、完整代码

1.unordered_map

2.unordered_set


一、哈希表的完善

1.优化:哈希函数

在实际使用中,往往需要以 字符串 作为存储依据(键值),比如 姓名 与 快递信息 、商品名称 与 价格中文单词 与 英文释义。

总之,字符串是一种非常常见的数据类型

而在我们实现的哈希表中,只考虑 整型 的存储情况,即直接用 key % capacity 计算哈希值,如果把整型换成 字符串 是会出问题的。

比如在下面这个场景中,程序无法编译

为了解决这个问题,我们可以将 获取 key 值 单独封装为一个 仿函数,再利用 模板特化,使其既能支持 整型 也能支持 字符串

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

// 特化string
template<>
struct HashFunc<string>
{
	// BKDR
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (auto& ch : s)
		{
			hash += ch;
		}
		return hash;
	}
};

添加了这个仿函数之后,就需要对 哈希表 中所有需要获取 key 的地方进行修改。

此时 哈希表 中的键值可以正常存储 字符串

三个字符串计算出的值分别为:140719561344

这是在 字符串长度不一且字符相差过大 的情况下计算出来的,假若 字符串过短或者字符串较为接近,导致字符串的每个字符的ascll值相加起来的和可能是 相同的值,这会导致 哈希冲突。

因此,单纯的累加每个字符的 ASCII 码值显得不够专业

有人专门对 字符串 进行研究,搞出了各种各样重复率较低的 字符串哈希算法

字符串哈希算法

在众多 字符串哈希算法 中,BKDRHash 一骑绝尘,各方面都非常优秀,因此这里我们选择 BKDRHash  算法作为 计算字符串值 的函数

BKDRHash 的核心就是 在原来值的基础上 *131,再加上字符的 ASCII 码值

// 特化string
template<>
struct HashFunc<string>
{
	// BKDR
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (auto& ch : s)
		{
			hash += ch;
			hash *= 131; 
		}
		return hash;
	}
};

修改之后,三个字符串计算出的值分别为:363451003702,显然此时的值更为分散,符合我们的需求


2.优化:素数大小

使用除留余数法时,哈希表的大小最好是素数,这样能够减少哈希冲突产生的次数。

SGI 版 STL 中,哈希表 在扩容时就使用了这一技巧。

简单来说,就是当我们扩容后,按照 下一个素数值大小 进行扩容,这些素数都是近似 2 倍的大小关系,在确保不会频繁扩容的同时,尽可能减少哈希冲突,所以需要这样一个函数。

// size_t newsize = GetNextPrime(_tables.size());
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];
}

同样的,需要对 扩容 的地方进行改造

 在改造之后,哈希表 的初始大小变为 53


3.新增:迭代器类

哈希表 中理应提供一个 迭代器 对其中的值进行判断,因为  是一个 单链表,只能向前走,不能回头,因此我们的 迭代器 要设计为 单向迭代器(只支持 ++

先来说说移动逻辑:

  • 如果当前所在桶中还有数据,简单,直接移动至 _next 即可
  • 如果没有数据(为空),就比较麻烦了,需要移动至当前哈希表中,下一个有数据的桶

显然,需要用到 哈希表,并且是 同一个哈希表
解决办法:构造迭代器时,传递当前哈希表的地址,构造一个指针指向哈希表

如何在 哈希表 中进行移动?
解决办法:首要问题是知道当前位于哈希表中的哪个位置。这个可以通过自己的 值 % 哈希表的大小 求出,清楚位置后,就向后移动,直到移动至一个不为空的位置,返回即可

 因为要获取使用 哈希表,所以需要对 迭代器类 做出一些调整

//前置声明
template<class K, class V>
class HashTable;
	
//迭代器类
template<class K, class V, class Ref, class Ptr>
struct HashTableIterator
{
	typedef HashNode<K, V> Node;	
	typedef HashTableIterator<K, V, Ref, Ptr> Self;	
	typedef HashTable<K, V> HT;	

	Node* _node;	
	const HT* _ht;	//指向哈希表的指针

	HashTableIterator(Node* node, const HT* ht)	
		:_node(node)
		, _pht(ht)
	{}
	
	//基本功能
	//……
};

现在能通过 _pht 访问同一个哈希表

细节:

  • 需要对哈希表进行前置声明才能正常使用
  • 指向哈希表的指针为 const 指针,否则 const 哈希表对象调不动迭代器

operator++()

Self& operator++()
{
	if (_node->_next != nullptr)
	{
		_node = _node->_next;
	}
	else
	{
		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;
}

在这个函数中,访问了 哈希表类 中的私有成员 _table,这是不行的,为了让其能成功访问,我们可以把 迭代器类 设为 哈希表类 的 友元类

template<class K, class V, class Ref, class Ptr>
friend struct HashTableIterator;	//友元声明

//迭代器相关
typedef HashTableIterator<K, V, pair<K, V>&, pair<K, V>*> iterator;
typedef HashTableIterator<K, V, const pair<K, V>&, const pair<K, V>*> const_iterator;

iterator begin()
{
	//起始位置是第一个有数据的桶
	Node* _node = nullptr;
	for (auto e : _table)
	{
		if (e != nullptr)
		{
			_node = e;
			break;
		}
	}

	return iterator(_node, this);	//构造迭代器对象
}

iterator end()
{
	//最后一个位置为空
	return iterator(nullptr, this);
}

const_iterator begin() const
{
	Node* _node = nullptr;
	for (auto e : _table)
	{
		if (e != nullptr)
		{
			_node = e;
			break;
		}
	}

	return const_iterator(_node, this);	//构造迭代器对象
}

const_iterator end() const
{
	return const_iterator(nullptr, this);
}

注意:

  • const 迭代器是为 const 对象提供的,所以可以选择重载 begin() 与 end(),也可以选择重新编写 cbegin() 和 cend(),二者除了函数名外,其他都是一样的
  • 单向迭代器是不能向后走的,所以哈希表中没有反向迭代器

二、封装实现 unordered_set 和 unordered_map

1.解决 k/v 参数冲突问题

unordered_set 需要 k 的模型,而 unordered_map 需要 k/v 的模型

为了满足 不同 的需求,需要对 哈希表 的模板进行调整,让其既能适应 unordered_set ,也能适应 unordered_map

至于如何调整,可以看看 红黑树 封装时的图示(类似的原理)

//节点类
template<class T>
struct HashNode
{
	T _data;
	HashNode<T>* _next;	
};

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

//迭代器类
template<class K, class T, class Ref, class Ptr>
struct HashTableIterator
{
	typedef HashNode<T> Node;	
	typedef HashTableIterator<K, T, Ref, Ptr> Self;	
	typedef HashTable<K, T> HT;	
	
	Node* _node;	
	const HT* _pht;	
};

//哈希表
template<class K, class T>
class HashTable
{
	typedef HashNode<T> Node;

	template<class K, class T, class Ref, class Ptr>
	friend struct HashTableIterator;	//友元声明
public:

	//迭代器相关
	typedef HashTableIterator<K, T, T&, T*> iterator;
	typedef HashTableIterator<K, T, const T&, const T*> const_iterator;

private:
	vector<Node*> _tables;// 节点指针数组
	size_t _n = 0;// 存储有效数据个数
};

2.解决 key 的获取问题

现在面临一个尴尬的问题:两个参数不同的类型,如何同时使用一种获取 key 的方法?

答案是:传递仿函数,根据自己的需求,创建仿函数,然后传给 哈希表,让 哈希表 在计算 key 时使用即可,当然 哈希表 中涉及获取 key 的地方都要改。

注意: 新增了迭代器之后,Find 的返回值变成了 iterator

对于 哈希表 类来说,主要改动其实就两个:模板参数的改变、获取哈希表对象 key 值

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

// 特化string
template<>
struct HashFunc<string>
{
	// BKDR
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (auto& ch : s)
		{
			hash += ch;
			hash *= 131; // 大佬定的值
		}
		return hash;
	}
};

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

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



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

	template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
	struct __HashIterator
	{
		typedef HashNode<T> Node;
		typedef HashTable<K, T, KeyOfT, Hash> HT;
		typedef __HashIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;
		typedef __HashIterator<K, T, T&, T*, KeyOfT, Hash> Iterator;

		Node* _node;
		const HT* _ht;

		__HashIterator(Node* node, const HT* ht)
			:_node(node)
			, _ht(ht)
		{}

		__HashIterator(const Iterator& it)
			:_node(it._node)
			, _ht(it._ht)
		{}

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

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

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

		Self& operator++()
		{
			if (_node->_next != nullptr)
			{
				_node = _node->_next;
			}
			else
			{
				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;
		}
	};


	template<class K, class T, class KeyOfT, class Hash>
	class HashTable
	{
		template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
		friend struct __HashIterator;

		typedef HashNode<T> Node;
	public:
		typedef __HashIterator<K, T, T&, T*, KeyOfT, Hash> iterator;
		typedef __HashIterator<K, T, const T&, const T*, KeyOfT, Hash> const_iterator;

		// 默认构造
		HashTable()
			:_tables()
			,_n(0)
		{}

		// 拷贝构造
		HashTable(const HashTable& ht)
			:_tables()
			, _n(0)
		{
			// 开辟空间
			_tables.resize(ht._tables.size());

			// 遍历插入节点
			for (auto& node : ht._tables)
			{
				Node* cur = node;
				while (cur)
				{
					Insert(cur->_data);
					cur = cur->_next;
				}
			}
		}
		
		// 赋值重载
		HashTable& operator=(HashTable ht)
		{
			_tables.swap(ht._tables);
			_n = ht._n;
		}

		iterator begin()
		{
			Node* cur = nullptr;
			// 找第一个不为空的节点
			for (size_t 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 (size_t 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);
		}

		~HashTable()
		{ 
			for (auto& cur : _tables)
			{
				while (cur)
				{
					Node* next = cur->_next; 
					delete cur;
					cur = next;
				}
				cur = nullptr;
			}
		}

		iterator Find(const K& key)
		{
			if (_tables.size() == 0)
				return end();

			Hash hash;
			KeyOfT kot;
			size_t hashi = hash(key) % _tables.size();// 取余找位置
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
					return iterator(cur, this);
				cur = cur->_next;
			}
			return end();
		}

		bool Erase(const K& key)
		{
			KeyOfT kot;
			Hash hash;
			size_t hashi = hash(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;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}

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

			iterator it = Find(kot(data));
			if (it != end())
				return make_pair(it, false);//冗余

			Hash hash;

			if (_n == _tables.size())
			{
				//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)
					{
						Node* next = cur->_next;
						size_t hashi = hash(kot(cur->_data)) % newtables.size();// 重新计算映射关系

						// 头插到新表
						cur->_next = newtables[hashi];
						newtables[hashi] = cur;
						cur = next;
					}
				}
				_tables.swap(newtables);
			}

			size_t hashi = hash(kot(data)) % _tables.size();// 取余找位置

			// 头插
			Node* newnode = new Node(data);
			newnode->_next = _tables[hashi];
			_tables[hashi] = newnode;
			++_n;

			return make_pair(iterator(newnode, this), false);
		}

		// size_t newsize = GetNextPrime(_tables.size());
		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];
		}

		// 最长桶长度
		size_t MaxBucketSize()
		{
			size_t max = 0;
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				auto cur = _tables[i];
				size_t size = 0;
				while (cur)
				{
					++size;
					cur = cur->_next;
				}
				// printf("[%d]->%d\n", i, size);
				if (size > max)
				{
					max = size;
				}
			}
			return max;
		}

	private:
		vector<Node*> _tables;// 节点指针数组
		size_t _n = 0;// 存储有效数据个数
	};
}

3.解决 unordered_set 迭代器非法操作

unordered_set 中只有 键值,而 键值 是不能被随意修改的(通过迭代器的方式)

void TestUS2()
{
	vector<int> arr = { 7,3,6,9,3,1,6,2 };
	unordered_set<int> s1(arr.begin(), arr.end());

	auto it = s1.begin();
	*it = 668;

	for (auto e : s1)
		cout << e << " "; // 668
	cout << endl;
}

结果为 668,这很正常,因为已经把迭代器中的键值改了,这就导致迭代器在移动时,是根据更改后的键值计算哈希值 = 668 % 53 = 32,而我们这组数中,32 位置及其后面都没有值,所以也就只打印了一个 668。

当然,这不是重点,重点在于 我们把 unordered_set 中的键值修改了!

库中的解决方法:不管你 unordered_set 申请的是什么迭代器,我都给你 const 迭代器

//迭代器
typedef typename HT::const_iterator iterator;
typedef typename HT::const_iterator const_iterator;

再次测试(此时需要把赋值语句屏蔽,否则影响后面的结果)

为什么要屏蔽赋值语句?

  • 因为接下来要展示的是一个编译时错误
  • 而给常量赋值这个错误优先级更高,在编译前就报错了,也就是说,不能让赋值语句报的错影响我们的操作

虽然最终都是报了不能随便赋值 的错误,但如果我们不借此根治问题,后续没有出现赋值语句时,一样会报错

此时出现了一个非常经典的 类型转换 错误

为什么?
这是因为 unordered_set 中 普通对象版的 begin()end() 使用的是 哈希表中 const 迭代器,但哈希表中的迭代器相关函数返回的是 普通迭代器 啊,也就是说,存在一个 普通迭代器 转为 const 迭代器 的问题,两者差别很大,编译器无法自行转换。

库中的解决方案:
在迭代器类中提供一个十分巧妙的函数,它对于 普通迭代器对象 来说,当传入的是 普通迭代器时,相当于 拷贝构造;当传入的是 const 迭代器时,相当于一个特殊的迭代器构造,即把 普通迭代器对象构造为 const 迭代器;当然,这个函数对于 const 迭代器对象 没有影响,毕竟这玩意不能被修改。

		__HashIterator(const Iterator& it)
			:_node(it._node)
			, _ht(it._ht)
		{}

加上之后,代码能正常编过,当然不能给常量赋值的错误也能正常显现
 

4.调整函数返回值

unordered_set 和 unordered_map 中的 insert() 返回值比较特殊,它不仅要返回 迭代器,也要表示本次插入操作 是否成功

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

        // unordered_set
		pair<iterator, bool> insert(const K& key)
		{
			return _ht.Insert(key);
		}

5.unordered_map 新增 operator[ ]

作为同时用于 键值 和 实值 的容器,unordered_map 需要一个能快速访问 实值 的函数,即 operator[]()

这个函数功能十分强大,具备:插入、修改、插入+修改、查找 等诸多功能,是 unordered_map 中的真香函数

实现逻辑:

  • 判断 key 存不存在,如果存在,返回 value
  • 如果不存在,就插入,并返回新的 value

可以分为几个判断写,也可以直接使用 insert()

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

三、完整代码

1.unordered_map

#pragma once


#include "HashTable.h"

namespace yrj
{
	template<class K, class V, class Hash = HashFunc<K>>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename HashBucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::iterator iterator;
		typedef typename HashBucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::const_iterator 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);
		}

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

		iterator find(const K& key)
		{
			return _ht.Find(key);
		}

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

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

	void test_unordered_map1()
	{
		unordered_map<int, int> m;
		m.insert(make_pair(1, 1));
		m.insert(make_pair(3, 3));
		m.insert(make_pair(2, 2));

		unordered_map<int, int>::iterator it = m.begin();
		while (it != m.end())
		{
			//it->first = 1;
			//it->second = 1;

			cout << it->first << ":" << it->second << endl;
			++it;
		}
		cout << endl;
	}

	void test_unordered_map2()
	{
		string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉", "梨" };
		unordered_map<string, int> countMap;
		for (auto& e : arr)
		{
			countMap[e]++;
		}

		for (auto& kv : countMap)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}

	class Date
	{
		friend struct HashDate;
	public:
		Date(int year = 1900, int month = 1, int day = 1)
			: _year(year)
			, _month(month)
			, _day(day)
		{}

		bool operator<(const Date& d)const
		{
			return (_year < d._year) ||
				(_year == d._year && _month < d._month) ||
				(_year == d._year && _month == d._month && _day < d._day);
		}

		bool operator>(const Date& d)const
		{
			return (_year > d._year) ||
				(_year == d._year && _month > d._month) ||
				(_year == d._year && _month == d._month && _day > d._day);
		}

		bool operator==(const Date& d) const
		{
			return _year == d._year
				&& _month == d._month
				&& _day == d._day;
		}

		friend ostream& operator<<(ostream& _cout, const Date& d);
	private:
		int _year;
		int _month;
		int _day;
	};

	ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}

	struct HashDate
	{
		size_t operator()(const Date& d)
		{
			size_t hash = 0;
			hash += d._year;
			hash *= 31;

			hash += d._month;
			hash *= 31;

			hash += d._day;
			hash *= 31;

			return hash;
		}
	};

// 一个类型要做unordered_map/unordered_set的key,要满足支持转换成取模的整形 和 ==比较
// 一个类型要做map/set的key,要满足支持<比较
/*if (cur->key < key)
{}
else if (key < cur->key)
{}
else
{}*/

	void test_unordered_map4()
	{
		Date d1(2023, 3, 13);
		Date d2(2023, 3, 13);
		Date d3(2023, 3, 12);
		Date d4(2023, 3, 11);
		Date d5(2023, 3, 12);
		Date d6(2023, 3, 13);

		Date a[] = { d1, d2, d3, d4, d5, d6 };

		unordered_map<Date, int, HashDate> countMap;
		for (auto e : a)
		{
			countMap[e]++;
		}

		for (auto& kv : countMap)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}

}

2.unordered_set

#pragma once

#include "HashTable.h"

namespace yrj
{
	template<class K, class Hash = HashFunc<K>>
	class unordered_set
	{
	public:
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

		typedef typename HashBucket::HashTable<K, K, SetKeyOfT, Hash>::const_iterator iterator;
		typedef typename HashBucket::HashTable<K, K, SetKeyOfT, Hash>::const_iterator 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:
		HashBucket::HashTable<K, K, SetKeyOfT, Hash> _ht;
	};

	void print(const unordered_set<int>& s)
	{
		unordered_set<int>::const_iterator it = s.begin();
		while (it != s.end())
		{
			//*it = 1;
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}

	void test_unordered_set1()
	{
		int a[] = { 3, 33, 2, 13, 5, 12, 1002 };
		unordered_set<int> s;
		for (auto e : a)
		{
			s.insert(e);
		}

		s.insert(54);
		s.insert(107);


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

		for (auto e : s)
		{
			cout << e << " ";
		}
		cout << endl;

		print(s);
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值