模拟实现unordered_map&unordered_set

模拟实现unordered_map&unordered_set

1. std::unordered_map 的定义与特性

所在头文件:<unordered_map>

std::unorederd_map 类模板:

template < class Key,                                    // unordered_map::key_type
           class T,                                      // unordered_map::mapped_type
           class Hash = hash<Key>,                       // unordered_map::hasher
           class Pred = equal_to<Key>,                   // unordered_map::key_equal
           class Alloc = allocator< pair<const Key,T> >  // unordered_map::allocator_type
           > class unordered_map;

1. unordered_map是存储<key, value>键值对的关联式容器,其允许通过keys快速的索引到与其对应的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. 它的迭代器至少是前向迭代器

7. 关联性:std::unorederd_map 是一个关联容器,其中的元素根据键来引用,而不是根据索引来引用。

8. 无序性:在内部,std::unordered_map中的元素不会根据其键值或映射值按任何特定顺序排序,而是根据其哈希值组织到桶中,以允许通过键值直接快速访问各个元素(常量的平均时间复杂度)。

9. 唯一性:std::unorederd_map中的元素的键是唯一的。

一些类型定义

类型成员	定义
key_type	第一个模板参数(Key)
mapped_type	第二个模板参数(T)
value_type	pair<const key_type,mapped_type>
hasher	第三个模板参数(Hash)
key_equal	第四个模板参数(Pred)

2. 构造 std::unordered_map:

在这里插入图片描述

3. 赋值操作

赋值方式函数声明
复制unordered_map& operator= ( const unordered_map& ump );
移动unordered_map& operator= ( unordered_map&& ump );
初始化列表unordered_map& operator= ( intitializer_list<value_type> il );

4. 迭代器操作

4.1 指向整个容器中的元素

函数声明解释
iterator begin() noexcept;const_iterator begin() const noexcept;返回一个迭代器,指向第一个元素
iterator end() noexcept;const_iterator end() const noexcept;返回一个迭代器,指向尾后元素
const_iterator cbegin() const noexcept;返回一个常量迭代器,指向第一个元素
const_iterator cend() const noexcept;返回一个常量迭代器,指向尾后元素

4.2 指向某个桶中的元素

函数声明解释
local_iterator begin( size_type n );const_local_iterator begin ( size_type n ) const;返回一个迭代器,指向第n个桶内的第一个元素
local_iterator end(size_type n);const_local_iterator end (size_type n) const;返回一个迭代器,指向第n个桶内的尾后元素
const_local_iterator cbegin( size_type n ) const;返回一个常量迭代器,指向第n个桶内的第一个元素
const_local_iterator cend( size_type n ) const返回一个常量迭代器,指向第n个桶内的尾后元素

5. 容量操作

函数声明解释
bool empty() const noexcept;unordered_map 是否为空
size_type size() const noexcept;获取unordered_map 中元素的数量

6. 访问操作

访问方式函数声明解释
使用方括号([])mapped_type& operator[] (const key_type& k);mapped_type& operator[] (key_type&& k);如果 k 匹配容器中某个元素的键,则该函数返回该映射值的引用。如果 k 与容器中任何元素的键都不匹配,则该函数将使用该键插入一个新元素,并返回该映射值的引用。
使用 at() mapped_type& at (const key_type& k);const mapped_type& at (const key_type& k) const;如果 k 匹配容器中某个元素的键,则该函数返回该映射值的引用。如果 k 与容器中任何元素的键都不匹配,则该函数将抛出 out_of_range 异常。

注意:const std::unordered_map 不能使用 operator[] 操作!

7. 插入操作

在这里插入图片描述

8. 删除操作

在这里插入图片描述

9. 查找操作

在这里插入图片描述

10. 桶操作

在这里插入图片描述

unordered_set的接口基本和unordered_map的接口一致,其没有operator[]操作

11. 模拟实现unordered_map

因为unordered_map的底层是使用的哈希表的结构,所以必须有一个哈希表
哈希原理

如果要按照前文当中的哈希表模拟实现unordered_map就需要进行改造。

11.1 哈希表的改造:

  1. 模板参数列表的改造
// K:关键码类型
// V: 不同容器V的类型不同,如果是unordered_map,V代表一个键值对,如果是
//unordered_set,V为 K
// KeyOfValue: 因为V的类型不同,通过value取key的方式就不同,详细见
//unordered_map/set的实现
// HF: 哈希函数仿函数对象类型,哈希函数使用除留余数法,需要将Key转换为整形数字才能取模
template<class K, class V, class KeyOfValue, class HF = DefHashF<T> >
class HashBucket;
  1. 增加迭代器操作:
// 为了实现简单,在哈希桶的迭代器类中需要用到hashBucket本身,
template<class K, class V, class KeyOfValue, class HF>
class HashBucket;
// 注意:因为哈希桶在底层是单链表结构,所以哈希桶的迭代器不需要--操作
template <class K, class V, class KeyOfValue, class HF>
struct HBIterator
{
	typedef HashBucket<K, V, KeyOfValue, HF> HashBucket;
	typedef HashBucketNode<V>* PNode;
	typedef HBIterator<K, V, KeyOfValue, HF> Self;
	HBIterator(PNode pNode = nullptr, HashBucket* pHt = nullptr);
	Self& operator++()
	{
		// 当前迭代器所指节点后还有节点时直接取其下一个节点
		if (_pNode->_pNext)
			_pNode = _pNode->_pNext;
		else
		{
			// 找下一个不空的桶,返回该桶中第一个节点
			size_t bucketNo = _pHt->HashFunc(KeyOfValue()(_pNode->_data))+1;
			for (; bucketNo < _pHt->BucketCount(); ++bucketNo)
			{
				if (_pNode = _pHt->_ht[bucketNo])
					break;
			}
		}
		return *this;
	}
	Self operator++(int);
	V& operator*();
	V* operator->();
	bool operator==(const Self& it) const;
	bool operator!=(const Self& it) const;
	PNode _pNode; // 当前迭代器关联的节点
	HashBucket* _pHt; // 哈希桶--主要是为了找下一个空桶时候方便
};
  1. 增加通过key获取value操作:
template<class K, class V, class KeyOfValue, class HF = DefHashF<T> >
class HashBucket
{
	friend HBIterator<K, V, KeyOfValue, HF>;
	// ......
public:
	typedef HBIterator<K, V, KeyOfValue, HF> Iterator;
	//
	// ...
	// 迭代器
	Iterator Begin()
	{
		size_t bucketNo = 0;
		for (; bucketNo < _ht.capacity(); ++bucketNo)
		{
			if (_ht[bucketNo])
				break;
		}
		if (bucketNo < _ht.capacity())
			return Iterator(_ht[bucketNo], this);
		else
			return Iterator(nullptr, this);
	}
	Iterator End(){ return Iterator(nullptr, this);}
	Iterator Find(const K& key);
	Iterator Insert(const V& data);
	Iterator Erase(const K& key);
	// 为key的元素在桶中的个数
	size_t Count(const K& key)
	{
		if(Find(key) != End())
			return 1;
		return 0;
	}
	size_t BucketCount()const{ return _ht.capacity();}
	size_t BucketSize(size_t bucketNo)
	{
		size_t count = 0;
		PNode pCur = _ht[bucketNo];
		while(pCur)
		{
			count++;
			pCur = pCur->_pNext;
		}
		return count;
	}
	// ......
};

HashTable.hpp

#pragma once
#include<vector>
#include"Common.hpp"

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

template<class T, class KorV, class DF = DFStr>
class HashBucket;

template<class T, class KorV,class DF = DFStr>
struct Iterator {
	typedef HashNode<T> Node;
	typedef Iterator<T, KorV, DF> Self;

	Iterator(Node* pNode = nullptr, HashBucket<T, KorV, DF>* ht = nullptr)
		:_pNode(pNode)
		,_ht(ht)
	{}
	T& operator*() {
		return _pNode->_data;
	}
	T* operator->() {
		return &(_pNode->_data);
	}
	Self& operator++() {
		Next();
		return *this;
	}
	Self operator++(int) {
		Self tmp(_pNode);
		Next();
		return tmp;
	}
	bool operator==(Self& t) {
		return _pNode == t._pNode;
	}
	bool operator!=(Self& t) {
		return _pNode != t._pNode;
	}
	void Next() {
		if (_pNode->_pNext) {
			_pNode = _pNode->_pNext;
		}
		else {
			size_t addr = _ht->HashFunc(_pNode->_data);
			for (int i = addr + 1; i < _ht->_arr.size(); ++i) {
				if (_ht->_arr[i]) {
					_pNode = _ht->_arr[i];
					return;
				}
			}
			_pNode = nullptr;
		}
	}
	Node* _pNode;
	HashBucket<T, KorV, DF>* _ht;
};

template<class T, class KorV, class DF>
class HashBucket {
public:
	typedef HashNode<T> Node;
	typedef HashBucket<T, KorV, DF> Self;
	typedef Iterator<T, KorV, DF> iterator;
	friend struct Iterator<T, KorV, DF>;
public:
	HashBucket(size_t size = 11)
		:_size(0)
	{
		_arr.resize(size);
	}
	~HashBucket() {
		Clear();
	}
	std::pair<iterator, bool> Insert(const T& data) {
		CheckCapacity();

		size_t addr = HashFunc(data);

		//查找是否有重复节点
		Node* ptr = _arr[addr];
		while (ptr) {
			if (KorV()(ptr->_data) == KorV()(data))   
				return make_pair(iterator(ptr, this), false);
			ptr = ptr->_pNext;
		}

		ptr = new Node(data);
		ptr->_pNext = _arr[addr];
		_arr[addr] = ptr;
		++_size;
		return make_pair(iterator(ptr, this), true);
	}
	size_t Erase(const T& data) {
		size_t addr = HashFunc(data);
		Node* ptr = _arr[addr];
		Node* pre = nullptr;
		while (ptr) {
			if (ptr->_data == data) {
				if (!pre) //删除头结点
					_arr[addr] = ptr->_pNext;
				else //删除中间节点
					pre->_pNext = ptr->_pNext;
				delete ptr;
				--_size;
				return 1;
				
			}
			else {
				pre = ptr;
				ptr = ptr->_pNext;
			}
		}
		return 0;
	}
	iterator find(const T& data) const{
		size_t addr = HashFunc(data);
		Node* ptr = _arr[addr];
		while (ptr) {
			if (KorV()(ptr->_data) == KorV()(data))
				return iterator(ptr, this);
			ptr = ptr->_pNext;
		}
		return iterator(nullptr, this);
	}
	size_t size() const{
		return _size;
	}
	bool empty()const {
		return _size == 0;
	}
	iterator begin() {
		for (int i = 0; i < _arr.size(); ++i) {
			if (_arr[i]) {
				return iterator(_arr[i], this);
			}
		}
		return iterator(nullptr, this);
	}
	iterator end() {
		return iterator(nullptr, this);
	}
	void Clear() {
		for (size_t i = 0; i < _arr.size(); ++i) {
			Node* ptr = _arr[i];
			while (ptr) {
				_arr[i] = ptr->_pNext;
				delete ptr;
				ptr = _arr[i];
			}
		}
		_size = 0;
	}
	size_t bucket_count() const{
		return _arr.size();
	}
	size_t bucket_size(size_t n) const{
		if (n >= _arr.size())
			return 0;
		size_t count = 0;
		Node* ptr = _arr[n];
		while (ptr) {
			++count;
			ptr = ptr->_pNext;
		}
		return count;
	}
	size_t bucket(const T& data) {
		return HashFunc(data);
	}
	void Swap(Self& t) {
		_arr.swap(t._arr);
		std::swap(_size, t._size);
	}
private:
	void CheckCapacity() {    //扩容
		if (_size == _arr.size()) {
			size_t newSize = GetNextPrime(_arr.size());
			Self newBucket(newSize);
			for (size_t i = 0; i < _arr.size(); ++i) {
				Node* ptr = _arr[i];
				while (ptr) {
					size_t addr = newBucket.HashFunc(ptr->_data);
					_arr[i] = ptr->_pNext;
					ptr->_pNext = newBucket._arr[addr];
					newBucket._arr[addr] = ptr;
					++newBucket._size;
					ptr = _arr[i];
				}
			}
			Swap(newBucket);
		}
	}
	size_t HashFunc(const T data) const{
		return DF()(KorV()(data)) % _arr.size();
	}
private:
	std::vector<Node*>  _arr;
	size_t _size;
};

unordered_map

#pragma once
#include"HashBucket.hpp"

template<class K,class V>
class Unordered_map {
	typedef std::pair<K, V> ValueType;
	typename typedef HashBucket<ValueType, KORV>::iterator iterator;
	struct KORV 
	{
		const K& operator()(const ValueType& data)const {
			return data.first;
		}
	};
public:
	Unordered_map(size_t size = 11)
		:_ht(size)
	{}
	iterator begin(){
		return _ht.begin();
	}
	iterator end(){
		return _ht.end();
	}
	bool empty()const {
		return _ht.empty();
	}
	size_t size()const {
		return _ht.size();
	}
	std::pair<iterator,bool> insert(const ValueType& data){
		return _ht.Insert(data);
	}
	size_t erase(const K& key){
		return _ht.Erase(ValueType(key,V()));
	}
	iterator find(const K& key) const{
		return _ht.find(ValueType(key, V()));
	}
	void clear() {
		_ht.Clear();
	}
	void swap(const Unordered_map<K,V>& m) {
		_ht.Swap(m._ht);
	}
	V& operator[](const K& k) {
		return (*(insert(ValueType(k, V())).first)).second;
	}
	size_t buck_count()const {
		return _ht.bucket_count();
	}
	size_t buck_size(size_t n)const {
		return _ht.bucket_size(n);
	}
	size_t bucket(const K& k) {
		return _ht.bucket(ValueType(k, V()));
	}
private:
	HashBucket<ValueType, KORV> _ht;
};

12. 模拟实现unordered_set

#pragma once
#include"HashBucket.hpp"

template<class K>
class Unordered_set {
	typedef K ValueType;
	typename typedef HashBucket<ValueType, KORV>::iterator iterator;
	struct KORV
	{
		const ValueType& operator()(const ValueType& data)const {
			return data;
		}
	};
public:
	Unordered_set(size_t size = 11)
		:_ht(size)
	{}
	iterator begin() {
		return _ht.begin();
	}
	iterator end() {
		return _ht.end();
	}
	bool empty()const {
		return _ht.empty();
	}
	size_t size()const {
		return _ht.size();
	}
	std::pair<iterator, bool> insert(const ValueType& data) {
		return _ht.Insert(data);
	}
	size_t erase(const ValueType& key) {
		return _ht.Erase(key);
	}
	iterator find(const ValueType& key) const {
		return _ht.find(key);
	}
	void clear() {
		_ht.Clear();
	}
	void swap(const Unordered_set<K>& m) {
		_ht.Swap(m._ht);
	}
	size_t buck_count()const {
		return _ht.bucket_count();
	}
	size_t buck_size(size_t n)const {
		return _ht.bucket_size(n);
	}
	size_t bucket(const ValueType& k) {
		return _ht.bucket(k);
	}
private:
	HashBucket<ValueType, KORV> _ht;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

wolf鬼刀

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

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

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

打赏作者

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

抵扣说明:

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

余额充值