模拟实现unordered_map和unordered_set超详解(C++)

在这里插入图片描述

😇 😇hello!我是bug。今天我们来讲讲unordered_map和unordered_set的相关内容:
(代码可能会有一点问题,请各位老铁指正 😘 😘 )

前言

🍉 🍉unordered_map和 unordered_set是一种专门用来进行搜索的容器或者数据结构,因为其底层是哈希表,所以其增删查改的时间复杂度都是O(1),其效率是很高的。注意:这里我们哈希表用的是拉链法,即数组里面存的是单链表的首元素指针,所以无法实现自减的重载,即哈希表不提供反向迭代器。


一、unordered_map

🌳 🌳区别:
🌱 unordered_map和map的功能类似,都是KV模型。
🌱 底层不同,map的底层是红黑树,unordered_map的底层是哈希表。
🌱 map有排序的功能,而unordered_map是无序的。
🌱 从效率上看,unordered_map增删查改的时间复杂度为O(1),而map增删查改的时间复杂度为O(logN)。
🌱 从空间上看,unordered_map消耗的空间比map空间更大。
🌱 使用场景:对数据有排序或者空间要求时,选择map;对效率有要求时,选择unordered_map。

🌵🌵 unordered_map的相关接口:

函数用法
operator=重载=
begin返回第一个元素的iterator
end返回最后一个元素下一个位置的iterator
empty判空
size返回元素个数
operator[]通过重载[]进行插入、修改
insert插入键对值
erase删除元素
clear清除元素
find查找元素
bucket_size返回桶中元素的个数
bucket_count返回桶的个数
bucket返回当前元素所处的桶数

二、unordered_set

🌳 🌳unordered_set和set都是K模型。因为它们的底层不同,所以其本质还是哈希表和红黑树的区别,区别在上面介绍unordered_map的时候谈到,这里不用过多篇幅。

🌵🌵 unordered_set的相关接口:

函数用法
operator=重载=
begin返回第一个元素的iterator
end返回最后一个元素下一个位置的iterator
empty判空
size返回元素个数
insert插入元素
erase删除元素
clear清除元素
find查找元素
bucket_size返回桶中元素的个数
bucket_count返回桶的个数
bucket返回当前元素所处的桶数

三、哈希表

🍒 🍒哈希表实现代码⬇️ ⬇️:

#pragma once
#include<iostream>
#include<vector>
#include<string>
#include<assert.h>

using std::cin;
using std::cout;
using std::endl;
using std::pair;
using std::vector;
using std::string;
using std::make_pair;

//选出key
template<class K, class V>
struct PairSelect1st
{
	const K& operator()(const pair<K, V>& kv) { return kv.first; }
};
template<class K>
struct KSelect1st
{
	const K& operator()(const K& k) { return k; }
};

//转成整型
template<class K>
struct HashFunc
{
	size_t operator()(const K& val) { return val; }
};
//模板的特化
template<>
struct HashFunc<std::string>
{
	size_t operator()(const std::string& s1)
	{
		size_t sum = 0;
		for (size_t i = 0; i < s1.size(); i++)
		{
			sum = sum * 131 + s1[i];
		}

		return sum;
	}
};

//比较判断
template<class K>
struct equal_to
{
	bool operator()(const K& lval, const K& rval) { return lval == rval; }
};
template<>
//模板特化
struct equal_to<string>
{
	bool operator()(const string& s1, const  string& s2) { return s1 == s2; }
};

//素数表
const int PRIMECOUNT = 28;
const size_t primeList[PRIMECOUNT] = {
 53ul, 97ul, 193ul, 389ul, 769ul,
 1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
 49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
 1610612741ul, 3221225473ul, 4294967291ul
};


namespace OpenHash
{
	template<class T>
	struct HashNode
	{
		typedef HashNode<T> Node;
		typedef HashNode<T>* pNode;

		HashNode<T>* _next;
		T _data;

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

	template<class K, class V, class T, class Pred, class Select1st, class HashFunc>
	class HashTable;

	template<class K, class V, class T, class Ref, class Ptr, class Pred, class Select1st, class HashFunc>
	struct Iterator
	{
		typedef HashNode<T> Node;
		typedef HashTable<K, V, T, Pred, Select1st, HashFunc> HashTable;
		typedef Iterator<K, V, T, Ref, Ptr, Pred, Select1st, HashFunc> self;

		Node* _pnode;
		HashTable* _pHT;

		Iterator(Node* pnode = nullptr, HashTable* pHT = nullptr) : _pnode(pnode), _pHT(pHT) {}

		Ref operator*() { return _pnode->_data; }
		const Ref operator*()const { return _pnode->_data; }
		Ptr operator->() { return &_pnode->_data; }
		const Ptr operator->()const { return &_pnode->_data; }

		self& operator++()
		{
			if (_pnode == nullptr)
				return *this;

			if (_pnode->_next != nullptr)
			{
				_pnode = _pnode->_next;
				return *this;
			}

			//_pnode->next == nullptr我们要去找现在的结点属于哪一个桶
			size_t index = HashFunc()(Select1st()(_pnode->_data)) % _pHT->_table.size() + 1;
			for (; index < _pHT->_table.size(); index++)
			{
				Node* cur = _pHT->_table[index];
				if (cur != nullptr)
				{
					_pnode = cur;
					return *this;
				}
			}
			_pnode = nullptr;
			return *this;
		}
		self operator++(int)
		{
			self tmp = *this;
			++(*this);
			return tmp;
		}

		bool operator!=(const self& it)const { return _pnode != it._pnode; }
		bool operator==(const self& it)const { return _pnode == it._pnode; }
	};

	template
		<class K, class V, class T, class Pred = equal_to<string>,
		class Select1st = PairSelect1st<K, V>, class HashFunc = HashFunc<K>>
		class HashTable
	{
		typedef HashNode<T>* pNode;
		typedef HashNode<T> Node;

		template<class K, class V, class T, class Ref, class Ptr, class Pred, class Select1st, class HashFunc>
		friend  struct Iterator;
	private:
		//存结点指针
		vector<pNode> _table;
		size_t _n;
	public:
		typedef Iterator<K, V, T, const T&, const T*, Pred, Select1st, HashFunc> const_iterator;
		typedef Iterator<K, V, T, T&, T*, Pred, Select1st, HashFunc> iterator;

		HashTable() :_n(0) {}

		void clear()
		{
			for (size_t index = 0; index < _table.size(); index++)
			{
				pNode cur = _table[index];
				pNode prev = cur;
				while (cur)
				{
					prev = cur;
					cur = cur->_next;
					delete prev;
					_table[index] = nullptr;
				}
			}
			_n = 0;
		}

		~HashTable()
		{
			clear();
		}

		iterator begin()
		{
			size_t index = 0;
			for (; index < _table.size(); index++)
			{
				pNode cur = _table[index];
				if (cur != nullptr)
					return iterator(cur, this);
			}
			return iterator(nullptr, this);
		}
		iterator end() { return iterator(nullptr, this); }

		const_iterator cbegin()
		{
			size_t index = 0;
			for (; index < _table.size(); index++)
			{
				pNode cur = _table[index];
				if (cur != nullptr)
					return const_iterator(cur, this);
			}
			return const_iterator(nullptr, this);
		}
		const_iterator cend() { return const_iterator(nullptr, this); }

		pair<iterator, bool> insert(const T& data)
		{
			//如果为空,则开空间
			if (!_table.size())
				_table.resize(53ul);
			//挑选key
			Select1st st1;
			//转换整型
			HashFunc hf;
			//判断是否冗余
			iterator ret = find(data);
			if (ret._pnode != nullptr)
				return std::make_pair(iterator(ret._pnode, this), false);

			//判断是否需要扩容
			if ((double)_n / (double)_table.size() >= 1)
			{
				vector<pNode> new_table(GetNextPrime(_table.size()));
				for (size_t i = 0; i < _table.size(); i++)
				{
					pNode cur = _table[i];
					if (cur != nullptr)
					{
						pNode next = _table[i];
						while (cur)
						{
							next = cur->_next;
							size_t new_index = (hf(st1(cur->_data))) % new_table.size();
							//头插
							cur->_next = new_table[new_index];
							new_table[new_index] = cur;
							cur = next;
						}
						_table[i] = nullptr;
					}
					//不推荐,插入的时候重新创建结点,浪费
					/*while(e != nullptr)
					{
						tmp.insert(e->_kv);
						e = e->_next;
					}*/
				}
				new_table.swap(_table);
			}

			//计算hashbucket的下标
			size_t index = hf(st1(data)) % _table.size();
			pNode newNode = new  Node(data);
			//头插
			newNode->_next = _table[index];
			_table[index] = newNode;
			_n++;

			return std::make_pair(iterator(newNode, this), true);
		}

		iterator find(const T& data)
		{
			HashFunc hf;
			Select1st slt;
			if (_table.size() == 0)
				return iterator(nullptr, this);

			size_t index = hf(slt(data)) % _table.size();
			pNode cur = _table[index];
			while (cur)
			{
				if (Pred()(slt(cur->_data), slt(data)))
					return iterator(cur, this);
				else
					cur = cur->_next;
			}

			return iterator(nullptr, this);
		}

		bool erase(const T& data)
		{
			Select1st st1;
			size_t index = HashFunc()(st1(data)) % _table.size();
			pNode cur = _table[index];
			pNode prev = cur;
			while (cur)
			{
				if (Pred()(st1(cur->_data), st1(data)))
				{
					//找到了
					if (cur == _table[index])//头结点
					{
						_table[index] = nullptr;
						_n--;
						delete cur;
						return true;
					}
					else
					{
						prev->_next = cur->_next;
						_n--;
						delete cur;
						return true;
					}
				}
				else//没找到
				{
					prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}

		size_t GetNextPrime(size_t prime)const
		{
			size_t i = 0;
			for (; i < PRIMECOUNT; i++)
			{
				if (primeList[i] > primeList[prime])
					return primeList[i];
			}

			return primeList[i];
		}

		size_t size() const { return _n; }
		bool empty()const { return _n == 0; }
		//返回桶的个数,即_table的大小
		size_t bucket_count()const { return _table.size(); }
		//返回桶里面的元素个数
		size_t bucket_size(size_t n)const
		{
			assert(n < _table.size());
			size_t count = 0;
			pNode cur = _table[n];
			while (cur)
			{
				cur = cur->_next;
				count++;
			}
			return count;
		}

		//返回当前key所在的桶数
		size_t bucket(const T& data)const 
		{
			size_t index =  HashFunc()(Select1st()(data)) % _table.size();
			pNode cur = _table[index];
			while (cur)
			{
				if (Pred()(Select1st()(cur->_data), Select1st()(data)))
					return index;
				else
					if (cur != nullptr)
						cur = cur->_next;
			}
			return -1;
		}
	};
}

😜 😜 哈希表之前我们实现过,这里就不进行详细介绍了。


四、unordered_map的模拟实现

🍒 🍒unordered_map模拟实现代码⬇️ ⬇️:

#pragma once
#include"HashTable.h"

namespace lz
{
	template<class K,class V,class Hash = HashFunc<K>,class equal = equal_to<K>>
	class Unordered_Map
	{
	private:
		OpenHash::HashTable<K, V, pair<K, V>,equal,PairSelect1st<K, V>, Hash> _ht;
		typedef Unordered_Map<K, V, Hash, equal> self;
	public:
		typedef  OpenHash::Iterator<K, V, pair<K, V>, pair<K, V>&, pair<K, V>*,
			equal, PairSelect1st<K, V>, HashFunc<K>> iterator;
		typedef OpenHash::Iterator<K, V, pair<K, V>, const pair<K, V>&, const pair<K, V>*,
			equal, PairSelect1st<K, V>, HashFunc<K>> const_iterator;
		Unordered_Map() :_ht() {}
		Unordered_Map(const self& s) { _ht = s._ht; }

		//迭代器
		iterator begin() { return _ht.begin(); }
		iterator end() { return _ht.end(); }
		const_iterator cbegin()const { return _ht.cbegin()._pnode->_data; }
		const_iterator cend()const { return _ht.cend()._pnode->_data; }

		//插入
		pair<iterator, bool> insert(const pair<K, V>& kv) { return _ht.insert(kv); }
		//删除
		bool erase(const iterator& it) { return _ht.erase(it._pnode->_data); }
		//查找
		iterator find(const pair<K, V>& kv) { return _ht.find(kv); }
		//元素个数
		size_t size()const { return _ht.size(); }
		//判空
		bool empty()const { return _ht.empty(); }
		//重载=
		self& operator=(const self& s) { _ht = s._ht; return *this; }
		//重载[]
		V& operator[](const K& key) { return insert(make_pair(key, V())).first._pnode->_data.second; }
		//清除
		void clear() { _ht.clear(); }
		//返回桶的个数
		size_t bucket_count()const { return _ht.bucket_count(); }
		//返回桶里面的元素
		size_t bucket_size(size_t n)const { return _ht.bucket_size(n); }
		//返回当前key所在的桶数
		size_t bucket(const pair<K, V>& kv)const{ return _ht.bucket(kv); }
	};

}

五、unordered_set的模拟实现

🍒 🍒unordered_set模拟实现代码⬇️ ⬇️:

#pragma once
#include"HashTable.h"

namespace lz
{
	template<class K,class Hash = HashFunc<K>, class equal = equal_to<K>>
	class Unordered_Set
	{
	private:
		OpenHash::HashTable<K, K, K, equal, KSelect1st<K>, Hash> _ht;
	public:
		typedef  OpenHash::Iterator<K, K, K, K&, K*,equal, KSelect1st<K>, Hash> iterator;
		typedef OpenHash::Iterator<K, K, K, const K&, const K*,equal, KSelect1st<K>, Hash> const_iterator;

		iterator begin() { return _ht.begin(); }
		iterator end() { return _ht.end(); }
		const_iterator cbegin() { return _ht.cbegin(); }
		const_iterator cend() { return _ht.cend(); }

		//插入
		pair<iterator, bool> insert(const K& key) { return _ht.insert(key); }
		//删除
		bool erase(const iterator& it) { return _ht.erase(it._pnode->_data); }
		//查找
		iterator find(const K& key) { return _ht.find(key); }
		//元素个数
		size_t size()const { return _ht.size(); }
		//清除
		void clear() { _ht.clear(); }
		//返回桶的个数
		size_t bucket_count()const { return _ht.bucket_count(); }
		//返回桶里面的元素
		size_t bucket_size(size_t n)const { return _ht.bucket_size(n); }
		//返回当前key所在的桶数
		size_t bucket(const K& key)const { return _ht.bucket(key); }
	};

}

六、测试代码

🍒 🍒测试代码⬇️ ⬇️:

#define _CRT_SECURE_NO_WARNING
#include"HashTable.h"
#include"Unordered_Map.h"
#include"Unordered_Set.h"
#include"BitSet.h"
#include"BloomFilter.h"

void Test_Unordered_Map1()
{
	lz::Unordered_Map<string, string> um1;
	pair<string, string> arr[] = {
	make_pair("left", "左边") ,make_pair("right", "右边"),make_pair("up", "向上")
	,make_pair("down", "向下"),make_pair("left","左边"),make_pair("eat","吃")
	,make_pair("sleep","睡觉"),make_pair("run","跑"),make_pair("jump","跳") };

	//测试判空
	cout << um1.empty() << endl;
	for (const auto& e : arr)
		um1.insert(e);

	//测试删除
	for (const auto& e : um1)
		cout << e.first << ":" << e.second << endl;
	um1.clear();
	for (const auto& e : um1)
		cout << e.first << ":" << e.second << endl;

	//通过[]插入
	um1["abc"] = "xx";
	for (auto str : um1)
		cout << str.first << ":" << str.second << endl;

	//修改
	um1["abc"] = "my";
	for (const auto& str : um1)
		cout << str.first << ":" << str.second << endl;
	cout << endl;

	//用引用,防止深拷贝
	for (const auto& e : arr)
		um1.insert(e);


	lz::Unordered_Map<string, string>::iterator it = um1.begin();
	while (it != um1.end())
	{
		um1.erase(it);
		it = um1.begin();
	}
	cout << endl;

	//测试bucket_count、bucket_size、bucket
	cout << um1.bucket_count() << endl;
	cout << um1.bucket(make_pair("left","左边")) << endl;//找不到就返回-1
	//cout << um1.bucket_size(um1.bucket(pair<string,string>("left","左边"))) << endl;
}

void Test_Unordered_Map2()
{
	lz::Unordered_Map<string, int> um1;
	string arr[] = { "苹果","梨","苹果","梨","梨","梨","苹果","香蕉","香蕉", "香蕉" ,"西瓜" };

	for (const auto& str : arr)
	{
		if (um1.find(make_pair(str, 1))._pnode == nullptr)
			um1.insert(make_pair(str, 1));
		else
			um1[str]++;
	}

	for (const auto& str : um1)
		cout << str.first << ":" << str.second << endl;
}

void Test_Unordered_Set()
{
	lz::Unordered_Set<string> us1;
	string arr[] = {
	"left", "左边" ,"right", "右边","up", "向上"
	,"down", "向下","left","左边","eat","吃"
	,"sleep","睡觉","run","跑","jump","跳" };
	//插入
	for (const auto& e : arr)
		us1.insert(e);

	for (const auto& str : us1)
		cout << str << " ";

	//删除
	lz::Unordered_Set<string>::iterator it = us1.begin();
	while (it != us1.end())
	{
		us1.erase(it);
		it = us1.begin();
	}
	cout << endl;

	//测试bucket_count、bucket_size、bucket
	cout << us1.bucket_count() << endl;
	cout << us1.bucket("left") << endl;
	//cout << us1.bucket_size(us1.bucket("left")) << endl;
}

void Test_BitSet()
{
	lz::BitSet bs1(200);
	bs1.set(100);
	bs1.set(100);
	bs1.set(0);
	//bs1.reset(100);
	//bs1.reset(0);
	cout << bs1.test(100) << endl;
	//cout << bs1.test(50) << endl;
	cout << bs1.size() << endl;
	cout << bs1.count() << endl;
}

void Test_BloomFilter()
{
	lz::BloomFilter<string> bf(18);
	string arr[] = {
	"left", "左边" ,"right", "右边","up", "向上"
	,"down", "向下","left","左边","eat","吃"
	,"sleep","睡觉","run","跑","jump","跳" };
	//插入
	for (const auto& str : arr)
		bf.insert(str);
	//判断
	for (const auto& str : arr)
		cout << bf.IsInBloomFilter(str) << " ";
	cout << bf.IsInBloomFilter("abc");
}

int main()
{
	//Test_BloomFilter();
	//Test_BitSet();
	//Test_Unordered_Map1();
	//Test_Unordered_Map2();
	//Test_Unordered_Set();
	return 0;
}

🍋 🍋 测试中包括了unordered_map的功能测试,unordered_map字典和计数的实现,以及unordered_set的功能测试。


😎 😎今天的内容到这里就结束了,希望各位小伙伴们能够有所收获,我们下期再见!
在这里插入图片描述

  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

今天也要写bug

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

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

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

打赏作者

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

抵扣说明:

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

余额充值