哈希实现&封装unordered系列容器

目录

一、前言

二、哈希桶

1、结点

2、哈希桶实现

(1)构造

(2)析构

(3)哈希仿函数

(4)迭代器实现

[<1> 构造](#<1> 构造)

[<2> operator*](#<2> operator*)

[<3> operator->](#<3> operator->)

[<4> operator!=](#<4> operator!=)

[<5> operator++](#<5> operator++)

(5)Begin

(6)End

[(7)Begin(const迭代器)](#(7)Begin(const迭代器))

[(8)End(const迭代器)](#(8)End(const迭代器))

(9)find

(10)erase

(11)insert

三、unordered_set

1、结构实现

2、insert

3、find

4、erase

5、迭代器

四、unordered_map

1、结构实现

2、insert

3、find

4、erase

[5、operator[ ]](#5、operator[ ])

6、迭代器

五、结语


一、前言

在STL中,unordered_set和unordered_map是基于哈希实现的高效容器,相比于红黑树实现的set、map而言,set、map走的是平衡二叉搜索树的查找路线,其查找效率为O(logN),而哈希实现的unordered_set和unordered_map提供了O(1)平均时间复杂度的查找,其中unordered_set用于存储唯一元素、unordered_map用于存储键值对,它们都属于C++11引入的无序容器,具有平均常数时间复杂度的插入、查找和删除效率。本文将围绕unordered系列容器展开介绍,并通过哈希桶来模拟实现,哈希桶通过存储一个链表来处理哈希冲突,当发生哈希冲突时,新元素会被添加到对应哈希桶的链表中,以此来处理哈希冲突。

二、哈希桶

1、结点

哈希桶结点实现为模板类型,实现为链表结构来解决哈希冲突,每个结点存放该结点的数据,T

cpp 复制代码
    template<class T>
	struct Hashnode
	{
		Hashnode(const T& data)
			:_data(data)
			, _next(nullptr)
		{
		}
		T _data;
		Hashnode<T>* _next;
	};

_data,以及指向下一个结点的_next指针,即Hashnode<T>*_next,Hashnode构造函数将_data初始化为data,_next初始化为nullptr,就完成了Hashnode结点的构造。

2、哈希桶实现

(1)构造

为了减少哈希冲突,哈希桶以素数容量进行扩容,通过内联函数_stl_next_prime来实现,函数中

cpp 复制代码
    inline unsigned long __stl_next_prime(unsigned long n)
    {
	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
	};
	const unsigned long* first = __stl_prime_list;
	const unsigned long* last = __stl_prime_list + __stl_num_primes;
	const unsigned long* pos = lower_bound(first, last, n);
	return pos == last ? *(last - 1) : *pos;
    }
    template<class K, class T, class KeyofT,class Hash = Hashfunc<K>>
	class Hashbucket
	{
        using Node = Hashnode<T>;
        Hashbucket()
			:_v(__stl_next_prime(0))
			, _n(0)
		{
		}
    private:
		vector<Node*> _v;
		size_t _n = 0;
	};
}

有一个素数数组_stl_prime_list,lower_bound用于取素数数组中不小于n的下一个素数,从而实现素数扩容,以此减少哈希冲突。Hashfunc将key的类型转化成无符号整形,using Node=Hashnode<T>,Node指代Hashnode<T>结点,哈希桶结构通过一个存放Node*的指针数组来实现,即vector<Node*> _v,_n表示有效哈希桶的个数,_v指针数组容量初始为_stl_next_prime(0)容量大小,_n初始化为0,就完成了哈希桶的构造。

(2)析构

哈希桶的析构,先处理单个哈希桶的析构,Node*cur=_v[i],若哈希桶不为nullptr,Node*next=cur->_next,先保存cur的下一个结点指针,delete cur,释放cur,cur=next,通过

cpp 复制代码
        ~Hashbucket()
		{
			for (size_t i = 0;i < _v.size();i++)
			{
				Node* cur = _v[i];
				while (cur)
				{
					Node* next = cur->_next;
					delete cur;
					cur = next;
				}
				_v[i] = nullptr;
			}
		}

while循环依次释放哈希桶一个链表的各个结点,_v[i]=nullptr,最后将_v[i]置为nullptr,通过for循环,逐个释放每个哈希桶链表的结点,就完成了哈希桶的析构。

(3)哈希仿函数

哈希仿函数Hashfunc用于将key转化为无符号整形,并减少哈希冲突,实现为模板类型,并对

cpp 复制代码
template<class K>
struct Hashfunc
{
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};
template<>
struct Hashfunc<string>
{
	size_t operator()(const string& s)
	{
		size_t hash = 0;
		for (auto ch : s)
		{
			hash += ch;
			hash *= 131;
		}
		return hash;
	}
};

string进行了特化,通过hash+=ch,hash*=131,使多位数据参与运算,以此来减少哈希冲突。

(4)迭代器实现

<1> 构造

哈希桶迭代器的实现可先用一个类型封装结点的指针,再通过重载运算符实现迭代器像指针一样访

cpp 复制代码
    template<class K,class T,class KeyofT,class Hash=Hashfunc<K>>
	class Hashbucket;//前置声明
	template<class K,class T,class Ref,class Ptr,class KeyofT,class Hash>
	struct HTiterator
	{
		using Node = Hashnode<T>;
		using HT = Hashbucket<K,T,KeyofT,Hash>;
		using Self = HTiterator<K,T, Ref, Ptr, KeyofT, Hash>;
		HTiterator(Node* PNode, const PHT* PHT)
			:_PNode(PNode)
			,_PHT(PHT)
		{}
        Node* _PNode;
		const HT* _PHT;
	};

问的行为,using Node=Hashnode<T>,using HT=Hashbucket<K,T,KeyofT,Hash>,using Self=HTiterator<K,T,Ref,Ptr,KeyofT,Hash>,Node指代Hashnode<T>结点,HT指代哈希桶Hashbucket<K,T,KeyofT,Hash>,Self指代迭代器HTiterator<K,T,Ref,Ptr,KeyofT,Hash>,需要注意的是哈希桶的迭代器是单向迭代器,除了结点的指针Node*_PNode,还需要有哈希桶对象的指针const HT*_PHT,这样如果当前桶走完了,要找到下一个哈希桶就容易多了,用key值计算出当前桶位置,依次往后找下一个不为空的桶即可,HTiterator构造函数将结点指针_PNode初始化为PNode,哈希桶指针_PHT初始化为PHT,就完成了迭代器的构造。

<2> operator*

operator*重载用于返回结点指针所指向的结点数据,即return _PNode->_data,返回类型为Ref,

cpp 复制代码
        Ref operator*()
		{
			return _PNode->_data;
		}

根据迭代器是普通迭代器还是const修饰的迭代器来确定Ref的类型,对于普通迭代器,Ref为T&,对于const迭代器,Ref为const T&,这样通过一个迭代器模板就可以一起实现普通迭代器和const迭代器了。

<3> operator->

operator->重载用于返回结点指针_PNode所指向的结点数据地址,即return &_PNode->_data,返

cpp 复制代码
        Ptr operator->()
		{
			return &_PNode->_data;
		}

回类型为Ptr,与operator*类似,根据迭代器是普通迭代器还是const修饰的迭代器来确定Ptr的类型,对于普通迭代器,Ptr为T*,对于const迭代器,Ptr为const T*。

<4> operator!=

operator!=重载用于判断迭代器是否相等,返回类型为bool,可通过迭代器的结点指针_PNode来进

cpp 复制代码
        bool operator!=(const Self& s)
		{
			return _PNode != s._PNode;
		}

行判断,即return _PNode!=s._PNode。

<5> operator++

operator++重载实现就较为复杂一些,首先判断当前哈希桶下面是否还有结点,若_PNode->_next

cpp 复制代码
        Self& operator++()
		{
			if (_PNode->_next)
			{
				_PNode = _PNode->_next;
			}
			else
			{
				Hash hash;
				KeyofT kot;
				size_t hash1 = hash(kot(_PNode->_data)) % (_PHT->_v.size());
				++hash1;
				while (hash1 < _PHT->_v.size())
				{
					_PNode = _PHT->_v[hash1];
					if (_PNode)
						break;
					else
						++hash1;
				}
				if (hash1 == _PHT->_v.size())
				{
					_PNode = nullptr;
				}
			}
			return *this;
		}

不为nullptr,则结点的指针指向下一个结点即可,即_PNode=_PNode->_next,若_PNode->_next为空,则表明当前哈希桶已经走完了,则需要找到下一个桶,这时就需借助_PHT哈希桶指针,KeyofT kot,kot用于取出_data的key,即kot(_PNode->_data),hash用于将key转化为无符号整形,即hash(kot(_PNode->_data)),通过key计算出当前哈希桶的位置,即size_t hash1=hash(kot(_PNode->_data))%(_PHT->_v.size()),再++hash1,通过while循环依次往后找下一个不为空的桶,循环条件为hash1<_PHT->_v.size(),_PNode=_PHT->_v[hash1],若_PNode不为空,则_PNode则为operator++的结果,else则++hash1,继续往下查找不为空的桶,while循环结束,则hash1==_PHT->_v.size(),表明已走到哈希桶结尾,则_PNode置为空,即_PNode=nullptr,最后return *this即可。

(5)Begin

实现了哈希桶的迭代器,就可以实现与迭代器相关的接口了,将HTiterator迭代器模板声明为友元

cpp 复制代码
    template<class K, class T, class KeyofT,class Hash = Hashfunc<K>>
	class Hashbucket
	{
		template<class K,class T,class Ref,class Ptr,class KeyofT,class Hash>
		friend struct HTiterator;
		using Node = Hashnode<T>;
		using Iterator = HTiterator<K,T, T&, T*, KeyofT,Hash>;
	public:
		Iterator Begin()
		{
			if (_n == 0)
			{
				return End();
			}
			for (int i = 0;i < _v.size();i++)
			{
				Node* cur = _v[i];
				if (cur)
				{
					return Iterator(cur, this);
				}
			}
			return End();
		}
		private:
		vector<Node*> _v;
		size_t _n = 0;
	};
}

关系,即friend struct HTiterator,方便访问其内部成员,HTiterator<K,T,T&,T*,KeyofT,Hash>即为普通迭代器,using Iterator=HTiterator<K,T,T&,T*,KeyofT,Hash>,Iterator指代普通迭代器HTiterator<K,T,T&,T*,KeyofT,Hash>,begin()返回第一个桶中第一个结点指针构造的迭代器,通过for循环进行查找,Node*cur=_v[i],若cur不为空,则返回cur和this指针所构造的迭代器,即return Iterator(cur,this)。

(6)End

End()返回的迭代器用nullptr来表示,返回nullptr和this指针所构造的迭代器,即return Iterator

cpp 复制代码
        Iterator End()
		{
			return Iterator(nullptr, this);
		}

(nullptr,this)。

(7)Begin(const迭代器)

const修饰的迭代器实现与普通迭代器类似,区别在于二者operator*、operator->返回类型的差异

cpp 复制代码
    template<class K, class T, class KeyofT,class Hash = Hashfunc<K>>
	class Hashbucket
	{
		template<class K,class T,class Ref,class Ptr,class KeyofT,class Hash>
		friend struct HTiterator;
		using Node = Hashnode<T>;
		using Iterator = HTiterator<K,T, T&, T*, KeyofT,Hash>;
		using Constiterator = HTiterator<K,T, const T&, const T*, KeyofT, Hash>;
	public:
        Constiterator Begin() const
		{
			if (_n == 0)
			{
				return End();
			}
			for (int i = 0;i < _v.size();i++)
			{
				Node* cur = _v[i];
				if (cur)
				{
					return Constiterator(cur, this);
				}
			}
			return End();
		}
        private:
		vector<Node*> _v;
		size_t _n = 0;
	};
}

对于普通迭代器,operator*、operator->返回的类型分别为T&、T*,而const修饰的迭代器,operator*、operator->返回的类型分别为const T&、const T*,HTiterator<K,T,const T&,const T*,KeyofT,Hash>即为const修饰的迭代器,using Constiterator=HTiterator<K,T,const T&,const T*,KeyofT,Hash>,用Constiterator指代const迭代器HTiterator<K,T,const T&,const T*,KeyofT,Hash>,const迭代器的Begin与普通迭代器类似,也是返回第一个桶中第一个结点指针构造的迭代器,若_n==0,则return End(),通过for循环进行查找,Node*cur=_v[i],若cur不为空,则返回cur和this指针所构造的const迭代器,即return Constiterator(cur,this),for循环结束,说明cur为nullptr,则return End()。

(8)End(const迭代器)

与普通迭代器类似,End()返回的const迭代器用nullptr表示,即返回nullptr和this指针所构造的

cpp 复制代码
        Constiterator End() const
		{
			return Constiterator(nullptr, this);
		}

const迭代器,即return Constiterator(nullptr,this)。

(9)find

find用于哈希桶数据的查找,hash实现将key转化成无符号整形,kot用于取出_data的key,先计算

cpp 复制代码
        Iterator find(const K& key)
		{
			Hash hash;
			KeyofT kot;
			size_t hash1 = hash(key) % (_v.size());
			Node* cur = _v[hash1];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return Iterator(cur,this);
				}
				cur = cur->_next;
			}
			return End();
		}

key的哈希值,即size_t hash1=hash(key) % (_v.size()),再根据该哈希值进行查找,即Node*cur=_v[hash1],通过while循环进行查找,若cur不为空,且kot(cur->_data)==key,则返回cur和this指针构造的迭代器,return Iterator(cur,this),else则cur=cur->_next,继续在该哈希桶里查找,while循环结束,则cur为nullptr,查找失败,return End()。

(10)erase

erase用于哈希桶结点的删除,先计算key的哈希值,即size_t hash1=key%(_v.size()),再将该哈希

cpp 复制代码
        bool erase(const K& key)
		{
			KeyofT kot;
			size_t hash1 = key % (_v.size());
			Node* prev = nullptr;
			Node* cur = _v[hash1];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev == nullptr)
					{
						_v[hash1] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}
					delete cur;
					--_n;
					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}

值位置的结点指针赋值给cur,即Node*cur=_v[hash1],prev充当cur的后继指针,用于标记cur的位置,便于删除后结点指针的连接,prev初始化为nullptr,通过while循环,KeyofT kot,kot用于取出_data的key,若kot(cur->_data)==key,且prev==nullptr,说明删除结点为哈希桶的首结点,则先将cur的_next指针赋值给_v[hash1],即_v[hash1]=cur->_next,再释放cur,delete cur,--_n,return true删除成功。若prev不为空,则说明删除结点为哈希桶的中间结点,则先将prev的_next指向cur的_next,即prev->_next=cur->_next,完成删除后结点的连接,再释放cur,delete cur,--_n,return true删除成功,else即kot(cur->_data)与key不相等,则继续在该哈希桶里查找,prev=cur,cur=cur->_next,while循环结束,则cur走到空,表明哈希桶里没有该数据,return false,删除失败。

(11)insert

insert用于哈希桶数据的插入,返回类型为pair<Iterator,bool>,若数据插入成功,则Iterator为插

cpp 复制代码
        pair<Iterator,bool> insert(const T& data)
		{
			KeyofT kot;
			Iterator it = find(kot(data));
			if(it!=End())
			{
				return {it,false};
			}
			Hash hash;
			if (_n == _v.size())
			{
				vector<Node*> newvn(__stl_next_prime(_v.size()+1));
				for (int i = 0;i < _v.size();i++)
				{
					Node* cur = _v[i];
					while (cur)
					{
						Node* next = cur->_next;
						size_t hash1 = hash(kot(cur->_data)) % (newvn.size());
						cur->_next = newvn[hash1];
						newvn[hash1] = cur;
						cur = next;
					}
					_v[i] = nullptr;
				}
				_v.swap(newvn);
			}
			size_t hash2 = hash(kot(data)) % (_v.size());
			Node* newnode = new Node(data);
			newnode->_next = _v[hash2];
			_v[hash2] = newnode;
			++_n;
			return {Iterator(newnode,this),true};
		}

入位置的迭代器,bool返回true,若插入失败,则Iterator返回已存在数据位置的迭代器,bool返回false,Iterator it=find(kot(data)),插入前先判断data是否已经在哈希桶中,若it!=End(),说明data已在哈希桶中,则插入失败,并返回其所在位置的迭代器,即return {it,false},else即it==End(),说明data不在哈希桶中,可以插入data,KeyofT kot,kot用于提取data的key,Hash hash,hash用于将key类型转化成无符号整形,插入前先判断哈希桶是否需要扩容,哈希桶一般在负载因子为1时进行扩容,即_n==_v.size()时进行扩容,采取素数容量进行扩容,以此减少哈希冲突,即vector<Node*> newvn(_stl_next_prime(_v.size()+1)),通过for循环,实现每个哈希桶的逐一拷贝,Node*cur=_v[i],cur通过while循环,实现哈希桶每个结点的拷贝,先保存cur的_next结点指针,Node*next=cur->_next,再计算cur在新哈希桶的哈希值,即size_t hash1=hash(kot(cur->_data))%(newvn.size()),结点采取头插方式,相比尾插,头插可直接访问哈希桶最近插入的数据,访问起来较为方便,即cur->_next=newvn[hash1],newvn[hash1]=cur。cur=next,继续访问原哈希桶的下一个结点,重复上面步骤,直到cur为空,则原哈希桶遍历结束,且原哈希桶的结点均已完成在新哈希桶上的指针连接,最后将原哈希桶的指针置为空,即_v[i]=nullptr,防止哈希桶结点的多次析构,最后将原哈希桶和新哈希桶交换,即_v.swap(newvn),就完成了哈希桶的扩容,扩容完成后,进行data的插入,先计算出data在哈希桶对应的哈希值,即size_t hash2=hash(kot(data))%(_v.size()),再进行头插,即Node*newnode=new Node(data),newnode->_next=_v[hash2],_v[hash2]=newnode,++_n,就完成了data的插入,返回插入位置的迭代器及bool值构造的pair<Iterator,bool>,即return {Iterator(newnode,this),true}。

三、unordered_set

1、结构实现

有了前面已实现的哈希桶结构,就可以借助哈希桶来实现unordered_set了,Hash_bucket::Hash

cpp 复制代码
#pragma once
#include"Hashtable.h"
namespace Kzy
{
	template<class K,class Hash=Hashfunc<K>>
	class unordered_set
	{
		struct SetkeyofT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
    private:
    Hash_bucket::Hashbucket<K, const K, SetkeyofT,Hash> _ht;
    };
}

bucket<K,const K,SetkeyofT,Hash> _ht,即为unordered_set底层的哈希桶,第1个模板参数K为unordered_set的find、erase接口所需的函数参数类型,第2个模板参数const K为unordered_set存放的数据类型,且unordered_set不支持修改,故需用const修饰。第3个模板参数SetkeyofT,SetkeyofT仿函数用于返回unordered_set的数据key,哈希桶模板参数这么设计是为了与unordered_map的实现兼容,Hash用于将key类型转化为无符号整形,这样就实现了unordered_set底层的哈希桶结构。

2、insert

有了哈希桶成员变量_ht,unordered_set的insert就可以直接调用_ht的insert接口来实现,即return

cpp 复制代码
        pair<iterator,bool> insert(const K& key)
		{
			return _ht.insert(key);
		}

_ht.insert(key)。

3、find

cpp 复制代码
        iterator find(const K& key)
		{
			return _ht.find(key);
		}

同理unordered_set的find直接调用_ht的find接口就可以实现,即return _ht.find(key)。

4、erase

cpp 复制代码
        bool erase(const K& key)
		{
			return _ht.erase(key);
		}

同理unordered_set的erase直接调用_ht的erase接口就可以实现,即return _ht.erase(key)。

5、迭代器

Hash_bucket::Hashbucket<K,const K,SetkeyofT,Hash>::Iterator即为普通迭代器,

cpp 复制代码
#pragma once
#include"Hashtable.h"
namespace Kzy
{
	template<class K,class Hash=Hashfunc<K>>
	class unordered_set
	{
		struct SetkeyofT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		using iterator = typename Hash_bucket::Hashbucket<K, const K, SetkeyofT,Hash>::Iterator;
		using const_iterator = typename Hash_bucket::Hashbucket<K, const K, SetkeyofT,Hash>::Constiterator;
		iterator begin()
		{
			return _ht.Begin();
		}
		iterator end()
		{
			return _ht.End();
		}
		const_iterator begin() const
		{
			return _ht.Begin();
		}
		const_iterator end() const
		{
			return _ht.End();
		}
   private:
		Hash_bucket::Hashbucket<K, const K, SetkeyofT,Hash> _ht;
  };
}

using iterator=typename Hash_bucket::Hashbucket<K,const K,SetkeyofT,Hash>::Iterator,iterator指代unordered_set的普通迭代器,typename用于声明类型,Hash_bucket::Hashbucket<K,const K,SetkeyofT,Hash>::Constiterator即为const修饰的迭代器,using const_iterator=typename Hash_bucket::Hashbucket<K,const K,SetkeyofT,Hash>::Constiterator,const_iterator指代unordered_set的const迭代器,同理unordered_set的普通和const迭代器的begin、end都可以直接调用_ht的Begin、End接口来实现,即return _ht.Begin(),return _ht.End()。

四、unordered_map

1、结构实现

与unordered_set实现类似,Hash_bucket::Hashbucket<K,pair<const K,V>,MapkeyofT,Hash> _ht即为unordered_map底层的哈希桶,第一个模板参数K为unordered_map的find、erase

cpp 复制代码
#pragma once
#include"Hashtable.h"
namespace Kzy
{
	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;
			}
		};
        private:
		Hash_bucket::Hashbucket<K, pair<const K, V>, MapkeyofT,Hash> _ht;
	};
}

接口所需的函数参数类型,第二个模板参数pair<const K,V>为unordered_map存放的数据类型,且key数据类型K不支持修改,故需用const修饰,第三个模板参数MapkeyofT,MapkeyofT仿函数用于返回unordered_map的数据key,即return kv.first,Hash用于将key类型转化为无符号整形,这样就实现了unordered_map底层的哈希桶结构。

2、insert

与unordered_set类似,有了底层哈希桶的结构,unordered_map的insert就可以直接调用_ht的

cpp 复制代码
        pair<iterator,bool> insert(const pair<K, V>& kv)
		{
			return _ht.insert(kv);
		}

insert接口来实现,即return _ht.insert(kv)。

3、find

cpp 复制代码
        iterator find(const K& key)
		{
			return _ht.find(key);
		}

同理unordered_map的find就可以直接调用_ht的find接口实现,即return _ht.find(key)。

4、erase

cpp 复制代码
        bool erase(const K& key)
		{
			return _ht.erase(key);
		}

同理unordered_map的erase就可以直接调用_ht的erase接口实现,即return _ht.erase(key)。

5、operator[ ]

由于unordered_map的operator[ ]结合了查找、插入和修改的功能,故可通过insert来实现operator

cpp 复制代码
        V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert({ key,V()});
			return ret.first->second;
		}

\],pair\ ret=insert({key,V()}),若key不在unordered_map中,则operator\[ \]将插入key和V的默认构造V()所构造的pair,若key存在,则插入失败,insert返回key所在的迭代器,bool为false,operator\[ \]返回pair\的second,也就是V\&,即return ret.first-\>second,这里省略了一层operator-\>(),即ret.first.operator-\>()-\>second,operator-\>在这种情况下可以省略。 ### 6、迭代器 Hash_bucket::Huckbucket\,MapkeyofT,Hash\>::Iterator即为 ```cpp #pragma once #include"Hashtable.h" namespace Kzy { template> class unordered_map { struct MapkeyofT { const K& operator()(const pair& kv) { return kv.first; } }; public: using iterator = typename Hash_bucket::Hashbucket, MapkeyofT,Hash>::Iterator; using const_iterator = typename Hash_bucket::Hashbucket, MapkeyofT,Hash>::Constiterator; iterator begin() { return _ht.Begin(); } iterator end() { return _ht.End(); } const_iterator begin() const { return _ht.Begin(); } const_iterator end() const { return _ht.End(); } private: Hash_bucket::Hashbucket, MapkeyofT,Hash> _ht; }; } ``` unordered_map的普通迭代器,using iterator=typename Hash_bucket::Hashbucket\,MapkeyofT,Hash\> ::Iterator,iterator指代unordered_map的普通迭代器,Hash_bucket::Hashbucket\,MapkeyofT,Hash\>::Constiterator即为const修饰的迭代器,using const_iterator=typename Hash_bucket::Hashbucket\,MapkeyofT,Hash\>::Constiterator,const_iterator指代unordered_map的const迭代器,同理unordered_map的普通和const迭代器的begin、end都可直接调用_ht的Begin、End接口来实现,即return _ht.Begin(),return _ht.End()。 ## 五、结语 本文主要围绕哈希桶的实现展开介绍,哈希桶通过一个存放结点指针的指针数组来实现,每个哈希桶都存储一个链表,当发生哈希冲突时,新元素就会被添加到对应哈希桶的链表中,需要注意的是哈希桶的迭代器为单向迭代器,因此在对迭代器进行封装时,除了结点指针,还需要有哈希桶的指针,这样在当前哈希桶走到空时,就可以通过哈希桶的指针来找到下一个不为空的哈希桶,这也是哈希桶迭代器实现的一大难点,哈希的本质是一种映射关系,首先将key类型转化为无符号整形,再通过哈希函数计算出哈希值,从而算出数据在哈希桶的位置,哈希桶的insert、find、erase的实现思路也都是基于哈希映射的思想。实现了哈希桶的结构,就可以哈希封装实现unordered_set和unordered_map了,只需确定哈希桶相应的模板参数就可以实现unordered_set和unordered_map,insert、find、erase实现只需直接调用哈希桶的相应接口即可,需要注意的是unordered_map的operator\[ \]实现,unordered_map的operator\[ \]结合了查找、插入、修改的功能,也是unordered_map使用频率较高的接口,哈希桶迭代器的实现采取泛型的思想,Ref,Ptr根据迭代器的类型来具体确定,这样通过一个迭代器模板就可以实现普通和const迭代器,减少了不必要的重复实现。相比于set、map,unordered_set、unordered_map具有O(1)的查找效率,因此在实践中应用较广泛,哈希实现与哈希封装unordered_set、unordered_map可谓是哈希映射思想的完美体现!

相关推荐
雾岛听蓝2 小时前
C++ vector:从使用到底层核心剖析
开发语言·c++
青岛少儿编程-王老师2 小时前
CCF编程能力等级认证GESP—C++7级—20251227
开发语言·c++
汉克老师2 小时前
GESP2025年12月认证C++四级真题与解析(编程题2 (优先购买))
c++·sort·结构体·优先级·gesp4级·gesp四级
我可以将你更新哟2 小时前
在Ubuntu 22.04上安装C++编译工具
linux·c++·ubuntu
Skrrapper2 小时前
TCPTP协议是什么?以及Socket使用指南
网络·c++·websocket·计算机网络
爱编程的小吴2 小时前
【力扣练习题】热题100道【哈希】189. 轮转数组
算法·leetcode·哈希算法
咔咔咔的2 小时前
840. 矩阵中的幻方
c++
wjykp2 小时前
105~108SVMf
算法