C++学习进阶:unordered_set和ma的实现

目录

前言 

 1.哈希表的结构

1.1.哈希节点 

1.2.迭代器的结构

1.2.1.普通迭代器

1.2.2.const迭代器的实现 

1.3.哈希表的实现

2.如何封装哈希表实现个性化的容器

2.1.unordered_set的封装

2.2.unordered_map的封装

3.以上内容的代码实现 

3.1.HashTable.h

3.2.unordered_set.h

3.3.unordered_map.h


前言 

我们在上一篇博客 数据结构与算法:哈希表-CSDN博客 中学习了哈希表这个数据结构,也对哈希桶有了一定的了解,学习这个数据结构的本质是为了对STL容器unordered_set和unordered_map进行深入的学习

我们在C知道中搜索“unordered_set和set 区别”,发现了这两个容器对应着不同的场景,为了更好的了解这个两个容器我们开始学习如何实现“unordered_set和map”把!!!

C++学习进阶:map和set的实现-CSDN博客

unordered_set和set是C++标准库中的两个容器,它们都用于存储一组唯一的元素。它们的主要区别在于底层实现和性能特点。

  1. 底层实现:

    • set是基于红黑树实现的有序容器,它可以保持元素的有序性。
    • unordered_set是基于哈希表实现的无序容器,它不会保持元素的有序性。
  2. 查找效率:

    • set的查找效率较高,时间复杂度为O(log n),因为它使用了红黑树作为底层数据结构。
    • unordered_set的查找效率更高,平均情况下时间复杂度为O(1),最坏情况下为O(n),因为它使用了哈希表作为底层数据结构。
  3. 插入和删除操作:

    • set的插入和删除操作相对较慢,时间复杂度为O(log n),因为需要维护红黑树的平衡性。
    • unordered_set的插入和删除操作相对较快,平均情况下时间复杂度为O(1),最坏情况下为O(n),因为需要处理哈希冲突。
  4. 元素顺序:

    • set中的元素按照键值自动排序,因此可以通过迭代器按照顺序访问元素。
    • unordered_set中的元素没有特定的顺序,因此无法通过迭代器按照顺序访问元素。

总结一下: set适用于需要保持元素有序性的场景,而unordered_set适用于对查找操作有较高要求的场景。选择哪个容器取决于具体的需求和性能要求

 1.哈希表的结构

古语云::合抱之木,生于毫末,九层之台,起于累土。对于unordered_set和map的初步模拟是一个很复杂的封装过程,就像是摆在你面前的高楼一般,我们知道很宏伟,但是更加知道这座大厦是有无数个小砖块堆砌而成的,我们实现这个STL容器也是如此,我们需要一步一步,先了解框架,然后一撮一撮土合为一块砖,然后再不断地,最终建成九层之台......

如图:实现这个哈希表我们需要

  1. 定义哈希节点,并且需要实现桶内的节点“增删查”三个函数
  2. 实现一个迭代器,实现const对象和普通对象的调用

1.1.哈希节点 

对于set和map的主要区别是:set传入的是key类型,map传入的是pair类型,所以第一步我们节点存放的数据就需要是一个多参数类型,因而需要引入模版参数。另外我们在哈希桶部分知道,桶的本质是一个链表所以这里我们也需要一个指针变量。这样子一个哈希节点就被抽象出来了!!

template<class T>
struct HashNode
{
	HashNode<T>* _next;
	// T可为key 也可为pair
	T _data;

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

因为增和查需要实现迭代器后才能实现,但是具体的内容我们已经在讲述哈希桶的那篇博客中进行了讲解,这里不在赘述。

1.2.迭代器的结构

  1. 完成一个普通迭代器,实现迭代器的基本功能,操作符重载,还有用什么结构实现
  2. 通过模版参数的设置,实现const迭代器

迭代器的部分我们由浅到深,先实现一个能用的普通迭代器,接着再实现const迭代器

1.2.1.普通迭代器

实现一个迭代器,我们首先要知道什么是迭代器,迭代器是干什么的?

首先,迭代器是一个类似于指针的结构,对于每一个哈希节点都会对应这维护一个迭代器节点,迭代器是用来遍历节点,并且访问节点的一个结构。

那么我们如何实现这个迭代器的结构呢?我们在上一篇博客中清楚地知道,遍历哈希表我们需要知道哈希表的size()和它的哈希桶位置Hash_i,所以我们需要传入哈希表来获得size和维护一个Hash_i来定位当前迭代器在哪一个哈希桶中。

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

	// 结构
	HashNode* _node;
	size_t _Hash_i;
	const HashTable* _pht;

	// 这里
	_HTIterator(HashNode* node, HashTable* pht, size_t Hash_i)
		:_node(node)
		, _pht(pht)
		, _Hash_i(Hash_i)
	{ }

	Self& operator++()
	{
		if (_node->_next != nullptr)
		{
			_node = _node->_next;
		}
		else
		{
			// 如果为空需要往下一个哈希桶寻找
			++_Hash_i;
            // 不断的找非空桶
			while (_Hash_i < _pht->_table.size())
			{
				if (_pht->_table[_Hash_i] != nullptr)
				{
					_node = _pht->_table[_Hash_i];
					break;
				}
				_Hash_i++;
			}

            // 都是空桶,当前节点即为最后一个桶的最后一个节点
			if (_Hash_i == _pht->_table.size())
			{
				_node = nullptr;
			}
		}
		return *this;
	}
	T& operator*() { return _node->_data; }
	bool operator!=(const Self& s) { return _node != s._node; }

};

代码讲解:

  1. 首先我们模版参数的设置,设置class KeyOfT和class Hash,两个模版参数均是为了实现仿函数内容,因为对于set和map,我们不知道传入哈希表的参数是key还是pair,对于set我们需要控制传入的参数仅仅为key,map为pair,于是乎我们可以在外部的set.h和map.h中各自实现一个仿函数来进行“各回各家,各找各妈”。Hash我们也是为了映射不同的变量类型,string和int这样子
  2. 对于迭代器++操作就是向后遍历,我们知道哈希桶的结构是存储链表头结点指针数组+哈希节点链表两个一体的,当我们向后遍历时会出现两种情况:1.当前所在节点的下一个节点不为空,也就是仍在原链表向下找。2.当前节点的下一个节点为空,当前节点为某个桶的尾节点,这时候我们需要向下一个桶遍历,走到下一个桶头节点处。这时如果我们更新后的当前桶下标小于哈希表的长度,如果这个桶为空就向后走找不为空的桶,如果桶不为空就维护数据然后break出去。

另外:这里我们访问了哈希表内的private对象_table,这里后面会在哈希表这个类中设置友元函数

到了这里一个普通的迭代器就实现好了,其实也不“难”(真的不难)。

1.2.2.const迭代器的实现 

我们还是从为什么需要const和怎么实现const迭代器来进行学习,对于迭代器来说我们可以修改迭代器对应哈希节点的数据,而在使用者的角度,有时我们只是想要遍历这个哈希桶,而不希望修改数据,所以开发者往往就会开发一份const迭代器来满足只写的需求,普通迭代器实现可读可写。

在哈希表结构中,const迭代器的具体体现,需要实现数据的指针、和引用不能修改

所以我们需要增加模版参数

template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>

 增加对数据的指针和引用,接下来我们就是实现一个const迭代器的构造函数

template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
struct _HTIterator
{
	typedef HashNode<T> HashNode;
	typedef _HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;
	typedef HashTable<K, T, KeyOfT, Hash> HashTable;

	// 结构
	HashNode* _node;
	size_t _Hash_i;
	const HashTable* _pht;

	// 普通迭代器的构造
	_HTIterator(HashNode* node, HashTable* pht, size_t Hash_i)
		:_node(node)
		, _pht(pht)
		, _Hash_i(Hash_i)
	{ }
    // const迭代器的构造
	_HTIterator(HashNode* node, const HashTable* pht, size_t Hash_i)
		:_node(node)
		, _pht(pht)
		, _Hash_i(Hash_i)
	{ }
	// 实现Insert的pair类型转换的拷贝构造
	_HTIterator(const _HTIterator<K, T, T&, T*, KeyOfT, Hash>& s)
		:_node(s._node)
		, _pht(s._pht)
		, _Hash_i(s._Hash_i)
	{ }

	Self& operator++()
	{
		if (_node->_next != nullptr)
		{
			_node = _node->_next;
		}
		else
		{
			// 如果为空需要往下一个哈希桶寻找
			++_Hash_i;
			while (_Hash_i < _pht->_table.size())
			{
				if (_pht->_table[_Hash_i] != nullptr)
				{
					_node = _pht->_table[_Hash_i];
					break;
				}
				_Hash_i++;
			}

			if (_Hash_i == _pht->_table.size())
			{
				_node = nullptr;
			}
		}
		return *this;
	}
	Ptr operator->() { return &_node->_data; }
	Ref operator*() { return _node->_data; }
	bool operator!=(const Self& s) { return _node != s._node; }

};

实现const迭代器不是主要,主要的是适配unordered_set和unordered_map这两个容器。

1.3.哈希表的实现

基本的架构“增删查”、构造和析构函数我们在上一个博客也是讲过了的,不再赘述,而迭代器我们维护时,通过它的默认构造来实现。

template<class K, class T, class KeyOfT, class Hash>
class HashTable
{
	typedef HashNode<T> HashNode;

	// 定义友元函数 提供HashTable的_table
	template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
	friend struct _HTIterator;

public:
    // 普通迭代器和const迭代器
	typedef _HTIterator<K, T, T&, T*, KeyOfT, Hash> iterator;
	typedef _HTIterator<K, T, const T&, const T*, KeyOfT, Hash> const_iterator;

	// 定义默认空间
	HashTable() { _table.resize(10); }

	~HashTable()
	{
		for (size_t i = 0; i < _table.size(); i++)
		{
			HashNode* current = _table[i];
			HashNode* next = nullptr;
			while (current != nullptr)
			{
				next = current->_next;
				delete current;
				current = next;
			}
			_table[i] = nullptr;
		}
	}

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

	// 增
	pair<iterator, bool> Insert(const T& data)
	{
		Hash hs_func;
		KeyOfT kot_func;

		iterator it = Find(kot_func(data));
		if (it != end())
			return make_pair(it, false);

		// 当负载因子为1时才进行扩容
		if (_num == _table.size())
		{
			vector<HashNode*> newT;
			newT.resize(_table.size() * 2);

			for (size_t i = 0; i < _table.size(); i++)
			{
				HashNode* current = _table[i];
				HashNode* next = nullptr;

				// 将旧表的内容插入进新表
				while (current != nullptr)
				{
					next = current->_next;
					// 头插逻辑
					size_t Hash_i = hs_func(kot_func(current->_data)) % newT.size();
					current->_next = newT[Hash_i];
					current = next;
				}
				_table[i] = nullptr;
			}

			_table.swap(newT);
		}

		size_t Hash_i = hs_func(kot_func(data)) % _table.size();
		HashNode* newNode = new HashNode(data);

		// 头插(针对哈希桶)
		// 新节点的下一个节点就是原本的头节点
		newNode->_next = _table[Hash_i];
		_table[Hash_i] = newNode;
		_num++;

		return make_pair(iterator(newNode, this, Hash_i), true);
	}
	// 删
	bool Erase(const K& key)
	{
		Hash hs_func;
		KeyOfT kot_func;

		size_t Hash_i = hs_func(key) % _table.size();

		HashNode* current = _table[Hash_i];
		HashNode* prev = nullptr;
		while (current != nullptr)
		{
			if (current->_kv.first == key)
			{
				if (prev == nullptr)
				{
					_table[Hash_i] = current->_next;
				}
				else
				{
					prev->_next = current->_next;
				}

				delete current;
				return true;
			}
			prev = current;
			current = current->_next;
		}

		return false;
	}
	// 查
	iterator Find(const K& key)
	{
		Hash hs_func;
		KeyOfT kot_func;
		size_t Hash_i = hs_func(key) % _table.size();

		HashNode* current = _table[Hash_i];

		while (current != nullptr)
		{
			if (kot_func(current->_data) == key)
				return iterator(current, this, Hash_i);

			current = current->_next;
		}

		return end();
	}

private:
	// 指针数组存储哈希桶的首元素地址
	vector<HashNode*> _table;
	// 存入的key的数目
	size_t _num = 0;
};

 需要注意的是:我们会发现Hash_i这个值的计算有时候不同,这个是根据实际的场景,来进行判断的

 到了这里哈希表的结构我们就初步完成了,代码会在后续模块统一给出!!!

2.如何封装哈希表实现个性化的容器

我们在前言中讲了我们学习哈希表就是为了实现并适配unordered_set和unordered_map这两个容器,也就是通过一份相同的哈希表代码来实现不同逻辑的unordered_set和unordered_map。废话不多说,我们开始吧

2.1.unordered_set的封装

如图unordered_set的封装所示,接下来我们解析一下原理:

  1. unordered_set的底层是哈希表,所以需要维护一个哈希表对象,这跟我们之前实现set封装需要维护一个红黑树对象一致
  2. 对于unordered_set来说,它维护的数据为key类型,并且这个key不可被修改,所以我们对于迭代器的使用默认就为const迭代器
  3. 对于大部分的函数我们只需要按照unordered_set的需求进行复用哈希表的原生提供的函数即可
  4. 值得注意的是当我们需要使用pair<iterator, bool>类型时,我们需要在迭代器中实现一个拷贝构造函数,让普通迭代器转化为const迭代器,实现权限的放大
  5. 对于仿函数struct SetKeyOfT我们让他只接受key类型的数据,也就是当为key时才进入这个仿函数,并返回内容

2.2.unordered_map的封装

这一部分我们讲一下这两个容器的区别与实现:

  1. 首先unordered_map这个容器的key也是不允许修改的,value允许修改,当处于普通迭代器时,我们通过将pair类型的first设置为const类型,并且将对应的迭代器的pair参数也设置为const类型 
  2. 在这里我们可以设置const迭代器来实现只读操作,防止value值被误修改。
  3. 对于哈希表的维护我们的参数为pair类型和set进行区别
  4. 最重要的一点我们的struct MapKeyOfT面对的是pair类型,这里也是封装精髓的体现,通过泛型编程来实现同一个底层结构实现不同原理的容器
  5. 这里还涉及[ ]重载具体看测试函数

可能看到这里大家会疑惑为什么直接给我看了结构,并没有教学如何实现?这个地方呢,因为主要是通过研究这两个容器的结构进行学习,讲起来需要长篇大论,我们这样子知道整体架构,理解为什么和怎么做即可。

3.以上内容的代码实现 

3.1.HashTable.h

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

// 整体实现的哈希函数
struct HashFunc
{
	size_t operator()(const size_t& key)
	{
		return (size_t)key; 
	}
	size_t operator()(const string& key)
	{
		size_t hash = 0;
		for (auto e : key)
		{
			hash *= 31;
			hash += e;
		}
		return hash;
	}
	// 也可以通过函数重载来实现,不过需要设置多种类型
};

namespace hash_bucket
{

	template<class T>
	struct HashNode
	{
		HashNode<T>* _next;
		// T可为key 也可为pair
		T _data;
		// STATUS _status = EMPTY;

		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 Ref, class Ptr, class KeyOfT, class Hash>
	struct _HTIterator
	{
		typedef HashNode<T> HashNode;
		typedef _HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;
		typedef HashTable<K, T, KeyOfT, Hash> HashTable;
		
		// 结构
		HashNode* _node;
		size_t _Hash_i;
		const HashTable* _pht;
		// const HashTable* _pht_const;

		// 这里
		_HTIterator(HashNode* node, HashTable* pht, size_t Hash_i)
			:_node(node)
			,_pht(pht)
			,_Hash_i(Hash_i)
		{ }
		_HTIterator(HashNode* node, const HashTable* pht, size_t Hash_i)
			:_node(node)
			, _pht(pht)
			, _Hash_i(Hash_i)
		{ }
		_HTIterator(const _HTIterator<K, T, T&, T*, KeyOfT, Hash>& s)
			:_node(s._node)
			, _pht(s._pht)
			, _Hash_i(s._Hash_i)
		{ }
		
		Self& operator++()
		{
			if (_node->_next != nullptr)
			{
				_node = _node->_next;
			}
			else
			{
				++_Hash_i;
				while (_Hash_i < _pht->_table.size())
				{
					if (_pht->_table[_Hash_i] != nullptr)
					{
						_node = _pht->_table[_Hash_i];
						break;
					}
					_Hash_i++;
				}

				if (_Hash_i == _pht->_table.size())
				{
					_node = nullptr;
				}
			}
			return *this;
		}
		Ptr operator->() { return &_node->_data; }
		Ref operator*() { return _node->_data; }
		bool operator!=(const Self& s) { return _node != s._node; }

	};

	template<class K, class T, class KeyOfT, class Hash>
	class HashTable
	{
		typedef HashNode<T> HashNode;

		// 定义友元函数 提供HashTable的_table
		template<class K, class T, class Ref, class Ptr, 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> const_iterator;

		// 定义默认空间
		HashTable() { _table.resize(10); }
		HashTable(const HashTable& ht)
		{
			_table = new vector<HashNode*>(ht._table);
			_num = ht._num;
		}

		~HashTable()
		{
			for (size_t i = 0; i < _table.size(); i++)
			{
				HashNode* current = _table[i];
				HashNode* next = nullptr;
				while (current != nullptr)
				{
					next = current->_next;
					delete current;
					current = next;
				}
				_table[i] = nullptr;
			}
		}
		
		// 迭代器
		iterator begin()
		{
			for (size_t i = 0; i < _table.size(); i++)
			{
				if (_table[i] != nullptr)
				{
					return iterator(_table[i], this, i);
				}
			}
			return end();
		}
		iterator end()
		{
			return iterator(nullptr, this, -1);
		}

		const_iterator begin() const
		{
			for (size_t i = 0; i < _table.size(); i++)
			{
				if (_table[i] != nullptr)
				{
					return const_iterator(_table[i], this, i);
				}
			}
			return end();
		}
		const_iterator end() const
		{
			return const_iterator(nullptr, this, -1);
		}
		
		// 增
		pair<iterator, bool> Insert(const T& data)
		{
			Hash hs_func;
			KeyOfT kot_func;

			iterator it = Find(kot_func(data));
			if (it != end())
				return make_pair(it, false);

			// 当负载因子为1时才进行扩容
			if (_num == _table.size())
			{
				vector<HashNode*> newT;
				newT.resize(_table.size() * 2);

				for (size_t i = 0; i < _table.size(); i++)
				{
					HashNode* current = _table[i];
					HashNode* next = nullptr;

					// 将旧表的内容插入进新表
					while (current != nullptr)
					{
						next = current->_next;
						// 头插逻辑
						size_t Hash_i = hs_func(kot_func(current->_data)) % newT.size();
						current->_next = newT[Hash_i];
						current = next;
					}
					_table[i] = nullptr;
				}

				_table.swap(newT);
			}

			size_t Hash_i = hs_func(kot_func(data)) % _table.size();
			HashNode* newNode = new HashNode(data);

			// 头插(针对哈希桶)
			// 新节点的下一个节点就是原本的头节点
			newNode->_next = _table[Hash_i];
			_table[Hash_i] = newNode;
			_num++;
			
			return make_pair(iterator(newNode, this, Hash_i), true);
		}
		// 删
		bool Erase(const K& key)
		{
			Hash hs_func;
			KeyOfT kot_func;

			size_t Hash_i = hs_func(key) % _table.size();

			HashNode* current = _table[Hash_i];
			HashNode* prev = nullptr;
			while (current != nullptr)
			{
				if (current->_kv.first == key)
				{
					if (prev == nullptr)
					{
						_table[Hash_i] = current->_next;
					}
					else
					{
						prev->_next = current->_next;
					}

					delete current;
					return true;
				}
				prev = current;
				current = current->_next;
			}

			return false;
		}
		// 查
		iterator Find(const K& key)
		{
			Hash hs_func;
			KeyOfT kot_func;
			size_t Hash_i = hs_func(key) % _table.size();

			HashNode* current = _table[Hash_i];

			while (current != nullptr)
			{
				if (kot_func(current->_data) == key)
					return iterator(current, this, Hash_i);

				current = current->_next;
			}

			return end();
		}

		// 对于哈希映射值的分析

		// 因为插入值时,需要知道 位置 和 类型,1.string还是int进行映射找到位置 2.为key还是kv结构,所以需要两个仿函数
 		// size_t Hash_i = hs_func(kot_func(key)) % _table.size();
		// 当查找和删除时,我们只需要知道 位置即可,类型我们通过后续判断 
		// size_t Hash_i = hs_func(key) % _table.size();

	private:
		// 指针数组存储哈希桶的首元素地址
		vector<HashNode*> _table;
		// 节点数
		size_t _num = 0;
	};
}


3.2.unordered_set.h

#pragma once
#include"HashTable.h"
namespace zhong
{
	template<class K, class Hash = HashFunc>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key) { return key;	}
		};
	public:
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT, HashFunc>::const_iterator iterator;
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT, HashFunc>::const_iterator const_iterator;

		pair<iterator, bool> insert(const K& key) { return _pht.Insert(key); }
		bool erase(const K& key) { return _pht.Erase(key); }
		iterator find(const K& key) { return _pht.Find(key); }
		// iterator begin() { return _pht.begin(); }
		// iterator end() { return _pht.end(); }

		const_iterator begin() const { return _pht.begin(); }
		const_iterator end() const { return _pht.end(); }
	private:

		hash_bucket::HashTable<K, K, SetKeyOfT, HashFunc> _pht;
	};
	// 测试函数
	void test1()
	{
		unordered_set<int> us;
		us.insert(1);
		us.insert(3);
		us.insert(5);
		unordered_set<int>::iterator it = us.begin();
		while (it != us.end())
		{
			// *it = 5;
			cout << *it << endl;
			++it;	
		}
		cout << endl;
	}
}

3.3.unordered_map.h

#pragma once
#include"HashTable.h"
namespace zhong
{

	template<class K, class V, class Hash = HashFunc>
	class unordered_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, HashFunc>::iterator iterator;
		typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, HashFunc>::const_iterator const_iterator;

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

		// 迭代器
		iterator begin() { return _pht.begin(); }
		iterator end() { return _pht.end(); }

		const_iterator begin() const { return _pht.begin(); }
		const_iterator end() const { return _pht.end(); }

		// 增删查
		pair<iterator, bool> insert(const pair<K, V>& kv) { return _pht.Insert(kv); }
		bool erase(const K& key) { return _pht.Erase(key); }
		iterator find(const K& key) { return _pht.Find(key); }
		
	private:

		hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, HashFunc> _pht;
	};
	// 测试函数
	void test2()
	{
		unordered_map<string, string> mss;
		mss.insert(make_pair("qq", "11"));
		mss.insert(make_pair("qa", "22"));
		mss.insert(make_pair("qw", "33"));
		unordered_map<string, string>::iterator its = mss.begin();
		while (its != mss.end())
		{
			cout << (*its).first << " " << (*its).second << endl;
			++its;
		}
		cout << endl;

		unordered_map<int, int> ms;
		ms.insert(make_pair(1, 1));
		ms.insert(make_pair(2, 2));
		ms.insert(make_pair(3, 3));
		unordered_map<int, int>::const_iterator it = ms.begin();
		while (it != ms.end())
		{
			// (*it).first = 1;
			// (*it).second = 0;
			cout << (*it).second << endl;
			++it;
		}
		cout << endl;
	}
	void test3()
	{
		string arr[] = { "apple", "xiaomi", "xiaomi", "huawei", "huawei" ,"huawei" };
		unordered_map<string, int> count_map;
		for (auto& e : arr)
		{
			count_map[e]++;
		}

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


}

  • 23
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值