C++ unordered_map

1. unordered系列关联式容器

在C++98 中, STL 提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到  \log_{2}N ,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好的查询是,进行很少的比较次数就能够将元素找到,因此在C++11 中, STL 又提供了 4 个unordered系列的关联式容器,这四个容器与红黑树结构的关联式容器使用方式基本类似,只是其底层结构不同,该系列容器使用哈希表进行封装。

2. unordered_map的介绍

1. unordered_map 是存储 <key, value> 键值对的关联式容器,其允许通过 key 快速的索引到与其对应的value
2. unordered_map 中,键值通常用于惟一地标识元素,而映射值是一个对象,其内容与此键关联。键和映射值的类型可能不同。
3. 在内部 ,unordered_map 没有对 <kye, value> 按照任何特定的顺序排序 , 为了能在常数范围内找到key 所对应的 value unordered_map 将相同哈希值的键值对放在相同的桶中。
4. unordered_map 容器通过 key 访问单个元素要比 map 快,但它通常在遍历元素子集的范围迭代方面效率较低。
5. unordered_maps 实现了直接访问操作符 (operator[ ]) ,它允许使用 key 作为参数直接访问value。
6. 它的迭代器至少是前向迭代器。

3. unordered_map的使用

1. unordered_map的构造

函数声明功能介绍
unordered_map()
构造不同格式的 unordered_map 对象

2. unordered_map的容量

函数声明
功能介绍
bool empty() const
检测 unordered_map 是否为空
size_t size() const
获取 unordered_map 的有效元素个数

3. unordered_map的迭代器

函数声明 功能介绍
iterator  begin()
返回 unordered_map 第一个元素的迭代器
iterator end()
返回 unordered_map 最后一个元素下一个位置的迭代器
const_iterator cbegin() const
返回 unordered_map 第一个元素的 const 迭代器
const_iterator cend() const
返回 unordered_map 最后一个元素下一个位置的 const 迭代器

4. unordered_map的元素访问

函数声明功能介绍
mapped_type& operator[ ] (const key_type& k)
返回与 key 对应的 value ,没有返回一个默认值
注意:该函数中实际调用哈希桶的插入操作,用参数 key V() 构造一个默认值往底层哈希桶中插入,如果key 不在哈希桶中,插入成功,返回 V() ,插入失败,说明 key 已经在哈希桶中,将key 对应的 value 返回。

5. unordered_map的查询

函数声明功能介绍
iterator find(const K& key)
返回 key 在哈希桶中的位置
size_t count(const K& key)
返回哈希桶中关键码为 key 的键值对的个数
注意: unordered_map key 是不能重复的,因此 count 函数的返回值最大为 1

6. unordered_map的修改操作

函数声明
功能介绍
pair<iterator,bool> insert (const value_type& x )
向容器中插入键值对

size_type erase ( const key_type& x )

删除容器中的键值对
void clear()
清空容器中有效元素个数
void swap(unordered_map&)
交换两个容器中的元

7. unordered_map的桶操作

函数声明
功能介绍
size_t bucket_count()const
返回哈希桶中桶的总个数
size_t bucket_size(size_t n)const
返回 n 号桶中有效元素的总个数
size_t bucket(const K& key)
返回元素 key 所在的桶号

4.unordered_map的模拟实现

首先我们要使用哈希表进行封装unordered_map即可,如下是HashTable.cpp的文件,有关哈希表的详细介绍,可以点击了解C++哈希表

#include <iostream>
#include <vector>
using namespace std;
namespace OpenHash
{
	template<class T>
	struct HashNode
	{
		T _data;
		HashNode* _next;
		HashNode(const T& data)
			:_data(data)
			, _next(nullptr)
		{}
	};
	//将key转换为整型方便取模
	template <class K>
	struct Hash
	{
		size_t operator()(const K& key)
		{
			return key;
		}
	};
	//模板特化,将string类型转换为整型
	template<>
	class Hash<string>
	{
		size_t operator()(const string& s)
		{
			size_t ret = 0;
			for (auto e : s)
			{
				ret = ret * 31 + e;
			}
			return ret;
		}
	};
	//实现迭代器
	//因为迭代器的实现需要借助HashTable,所以需要前置定义
	template <class K, class T, class KeyOfT, class HashFunc = Hash<K>>
	class HashTable;
	template <class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc = Hash<K>>
	struct HashTableIterator
	{
		typedef typename HashNode<T> Node;
		typedef typename HashTableIterator<K, T, Ptr, Ref, KeyOfT, HashFunc> Self;
		typedef typename HashTable<K, T, KeyOfT, HashFunc> HashTable;

		HashTable* _ht;
		Node* _node;
		HashTableIterator() = default;
		HashTableIterator(const Node*& node, const HashTable*& ht)
			:_ht(ht)
			, _node(node)
		{}
		Self& operator++()
		{
			//遍历当前桶
			if (_node->_next)
				_node = _node->_next;
			//找下一个桶
			else
			{
				KeyOfT kot;
				HashFunc hf;
				//获取索引值
				size_t index = hf(kot(_node->_data)) % _ht->_table.size();
				++index;
				while (index < _ht->_table.size() && _ht->_table[index] == nullptr)
					++index;
				if (index == _ht->_table.size())
					_node = nullptr;
				else
					_node = _ht->_table[index];
			}
			return *this;
		}
		Ref operator*()
		{
			return _node->_data;
		}
		Ptr operator->()
		{
			return &_node->_data;
		}
		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 HashFunc>
	class HashTable
	{
	public:
		//友元(因为iterator需要用到_table)
		template <class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc>
		friend struct HashTableIterator;
		typedef typename HashNode<T> Node;
		typedef typename HashTableIterator<K, T, T*, T&, KeyOfT, HashFunc> iterator;
		//构造函数
		HashTable()
			:_table(vector<Node*>())
			, _n(0)
		{}
		iterator begin()
		{
			for (size_t i = 0; i < _table.size(); i++)
			{
				if (_table[i])
					return iterator(_table[i], this);
			}
			return iterator(nullptr, this);
		}
		iterator end()
		{
			return iterator(nullptr, this);
		}
		iterator find(const K& key)
		{
			if (_table.size() == 0)
			{
				return iterator(nullptr, this);
			}
			KeyOfT kot;
			HashFunc hf;
			size_t index = hf(key) % _table.size();
			Node* cur = _table[index];
			while (cur)
			{
				if (kot(cur->_data) == key)
					return iterator(cur, this);
				cur = cur->_next;
			}
			return iterator(nullptr, this);
		}
		pair<iterator, bool> insert(const K& key)
		{
			//第一次插入需要扩容
			if (_table.size() == 0)
				_table.resize(10);
			//不能出现重复数据
			if (find(key) != iterator(nullptr, this))
			{
				return make_pair(find(key), false);
			}
			KeyOfT kot;
			HashFunc hf;
			//桶的个数是一定的,随着元素的不断插入,每个桶中元素的个数不断增多,极端情况下,
			//可能会导致一个桶中链表节点非常多,会影响的哈希表的性能,因此在一定条件下需要对
			//哈希表进行增容,那该条件怎么确认呢?开散列最好的情况是:每个哈希桶中刚好挂一个
			//节点,再继续插入元素时,每一次都会发生哈希冲突,因此,在元素个数刚好等于桶的个数
			//时,可以给哈希表增容。
			//负载因子到1,需要扩容
			if (_n == _table.size())
			{
				vector<Node*> newtable;
				newtable.resize(_table.size() * 2);
				//重新映射到新表
				for (auto e : _table)
				{
					Node* cur = e;
					while (cur)
					{
						size_t index = hf(kot(cur->_data)) % newtable.size();
						cur->_next = newtable[index];
						newtable[index] = cur;
						cur = cur->_next;
					}
				}
			}
			size_t index = hf(key) % _table.size();
			Node*& cur = _table[index];
			while (cur)
				cur = cur->_next;
			cur = new Node(key);
			return make_pair(iterator(cur, this), true);
		}
		bool erase(const K& key)
		{
			KeyOfT kot;
			HashFunc hf;
			size_t index = hf(key) % _table.size();
			Node* cur = _table[index], * pre = _table[index];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					//要删除该位置第一个元素
					if (cur == pre)
						_table[index] = cur->_next;
					else
						pre->_next = cur->_next;

					delete cur;
					_n--;
					return true;
				}
				pre = cur;
				cur = cur->_next;
			}
			return false;
		}
	private:
		vector<Node*> _table;
		size_t _n;//有效数据个数
	};
}

unordered_map.cpp文件如下

#include"HashTable.cpp"
namespace lbk
{
	template<class K,class Hash=OpenHash::Hash<K>>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename OpenHash::HashTable<K,K,SetKeyOfT,Hash>::iterator iterator;
		iterator begin()
		{
			return _ht.begin();
		}
		iterator end()
		{
			return _ht.end();
		}
		iterator find(const K& key)
		{
			return _ht.find(key);
		}
		pair<iterator, bool> insert(const K& key)
		{
			return _ht.insert(key);
		}
		bool erase(const K& key)
		{
			return _ht.erase(key);
		}
	private:
		OpenHash::HashTable<K, K, SetKeyOfT, Hash> _ht;
	};
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我要满血复活

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值