封装unordered_set&&unordered_map

这篇具有很好参考价值的文章主要介绍了封装unordered_set&&unordered_map。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

注:实现unordered系列容器是为了学习,因此并非将全部接口实现,所以只实现部分接口


目录

哈希表的改造

模板参数列表的改造

增加迭代器操作

增加通过T获取value操作

HashTable.h的修改

unordered_set的封装

unordered_map的封装


哈希表的改造

模板参数列表的改造

  • K:关键码类型
  • T:不同容器T的类型不同,如果是unordered_map,T代表一个键值对;如果是unordered_set,V为K
  • KeyOfT:因为V的类型不同,通过T取key的方式就不同
  • HashFunc:哈希函数仿函数对象类型,哈希函数使用除留余数法,需要将Key转换为整形数字才能取模
template<class K, class T, class KeyOfT, class HashFunc = DefaultfHash<T>>
class HashTable;

增加迭代器操作

    //声明节点类,否则迭代器无法识别
	template<class T>
	struct HashNode;
	//声明哈希表结构,否则迭代器无法识别
	template<class K, class T, class KeyOfT, class HashFunc>
	struct HashTable;

    //迭代器实现
	template<class K, class T, class KeyOfT, class HashFunc>
	class __HTIterator
	{
		typedef HashNode<T> Node;//节点
		typedef __HTIterator<K, T, KeyOfT, HashFunc> iterator;//返回迭代器
	public:
		Node* _node;
		HashTable<K, T, KeyOfT, HashFunc>* _pht;//跳向下一个桶需要
		
        //构造函数
		__HTIterator()
		{}
		__HTIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			, _pht(pht)
		{}

		iterator& operator++()
		{
			//分别处理同一个桶中迭代和跳向下一个桶迭代
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				size_t hashi = HashFunc()(KeyOfT()(_node->_data)) % _pht->_table.size();
				++hashi;
				for (; hashi < _pht->_table.size(); ++hashi)
				{
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						break;
					}
				}
				//走到末尾
				if (hashi == _pht->_table.size())
					_node = nullptr;
			}

			return *this;
		}
        //返回数据
		T& operator*()
		{
			return _node->_data;
		}
        //返回数据的地址
		T* operator->()
		{
			return &_node->_data;
		}

		bool operator==(const iterator& it) const
		{
			return _node == it._node;
		}

		bool operator!=(const iterator& it) const
		{
			return _node != it._node;
		}
	};

增加通过T获取value操作

	//K针对find和erase需要使用
	//T是针对上层unordered_set传入的是K而unordered_map传入的是pair<K, V>做出更高维度泛型的处理
	//KeyOfT是提取出vaule中的unordered_set和unordered_map中的K
	//HashFunc哈希函数
	template<class K, class T, class KeyOfT, class HashFunc>
	struct HashTable
	{
		typedef HashNode<T> Node;
		//迭代器实现中需要访问哈希表中的私有成员
		template<class K, class T, class KeyOfT, class HashFunc>
		friend class __HTIterator;
	public:
		typedef __HTIterator<K, T, KeyOfT, HashFunc> iterator;
		//析构函数需要自己实现,vector存的是内置类型,不会释放节点,只会释放表
		~HashTable()
		{
			for (int i = 0; i < _table.size(); ++i)
			{
				Node* cur = _table[i];
				while (cur)
				{
					Node* next = cur->_next;
					if (cur)
						delete cur;
					cur = next;
				}
				_table[i] = nullptr;
			}
		}

		//需要寻找第一个存储数据的位置
		iterator begin()
		{
			for (int i = 0; i < _table.size(); ++i)
			{
				Node* cur = _table[i];
				while (cur)
					if (cur)
						return iterator(cur, this);
			}

			return iterator(nullptr, this);
		}

		//末尾为nullptr
		iterator end()
		{
			return iterator(nullptr, this);
		}

		pair<iterator, bool> Insert(const T& data)
		{
			//不存入key值相同数据
			iterator pos = Find(KeyOfT()(data));
			if (pos != end())
				return make_pair(pos, false);

			//负载因子大小和表的大小一样大的时候扩容
			if (_table.size() == _n)
			{
				size_t newsize = _table.size() == 0 ? 10 : _table.size() * 2;
				vector<Node*> newtable;
				newtable.resize(newsize, nullptr);
				//遍历旧表,将旧表中的节点插入到新表中
				for (int i = 0; i < _table.size(); ++i)
				{
					Node* cur = _table[i];
					while (cur)
					{
						Node* next = cur->_next;//存储next方便更新cur
						//寻找该节点在新表中的位置
						size_t hashi = HashFunc()(KeyOfT()(cur->_data)) % newsize;
						//单链表的头插
						cur->_next = newtable[hashi];//将待插入节点的next指向待插入位置的第一个节点
						newtable[hashi] = cur;//将待插入节点插入到待插入位置
						cur = next;//更新cur
					}
					_table[i] = nullptr;//将旧表位置置空
				}
				_table.swap(newtable);//将处理好的数据交换给旧表
			}

			//经过上面处理后将数据存入
			size_t hashi = HashFunc()(KeyOfT()(data));
			hashi %= _table.size();
			//将节点头插到链表中
			Node* newnode = new Node(data);
			newnode->_next = _table[hashi];
			_table[hashi] = newnode;

			++_n;

			return make_pair(iterator(newnode, this), true);
		}

		iterator Find(const K& key)
		{
			//表空无法查找
			if (_table.size() == 0)
				return iterator(nullptr, this);

			size_t hashi = HashFunc()(key);
			hashi %= _table.size();

			//从计算到位置处开始查询该处链表中是否存在该key
			Node* cur = _table[hashi];
			while (cur)
			{
				if (KeyOfT()(cur->_data) == key)
					return iterator(cur, this);
				cur = cur->_next;
			}

			return iterator(nullptr, this);
		}

		bool Erase(const K& key)
		{
			//表为空无法删除
			if (_table.size() == 0)
				return false;

			size_t hashi = HashFunc()(key);
			hashi %= _table.size();

			//找到对应位置后进行单链表的删除
			Node* cur = _table[hashi];
			Node* prev = nullptr;
			while (cur)
			{
				//单链表的头删和普通删除
				if (KeyOfT()(cur->_data) == key)
				{
					if (prev == nullptr)
						_table[hashi] = cur->_next;
					else
						prev->_next = cur->_next;
					delete cur;
					return true;
				}
				prev = cur;
				cur = cur->_next;
			}

			return false;
		}

	private:
		vector<Node*> _table;//每个位置存储链表
		size_t _n = 0;//负载因子
	};

以上是对哈希表的改造,将以上的实现和修改放在一起

HashTable.h的修改

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

//哈希函数
template<class K>
struct DefaultHash
{
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};

template<>
struct DefaultHash<string>
{
	size_t operator()(const string& key)
	{
		size_t hashi = 0;
		for (auto& ch : key)
			hashi = hashi * 131 + ch;
		return hashi;
	}
};

namespace Bucket
{
	//声明节点类,否则迭代器无法识别
	template<class T>
	struct HashNode;
	//声明哈希表结构,否则迭代器无法识别
	template<class K, class T, class KeyOfT, class HashFunc>
	struct HashTable;

	//迭代器实现
	template<class K, class T, class KeyOfT, class HashFunc>
	class __HTIterator
	{
		typedef HashNode<T> Node;
		typedef __HTIterator<K, T, KeyOfT, HashFunc> iterator;
	public:
		Node* _node;
		HashTable<K, T, KeyOfT, HashFunc>* _pht;//跳向下一个桶需要
		
		__HTIterator()
		{}

		__HTIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			, _pht(pht)
		{}

		iterator& operator++()
		{
			//分别处理同一个桶中迭代和跳向下一个桶迭代
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				size_t hashi = HashFunc()(KeyOfT()(_node->_data)) % _pht->_table.size();
				++hashi;
				for (; hashi < _pht->_table.size(); ++hashi)
				{
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						break;
					}
				}
				//走到末尾
				if (hashi == _pht->_table.size())
					_node = nullptr;
			}

			return *this;
		}

		T& operator*()
		{
			return _node->_data;
		}

		T* operator->()
		{
			return &_node->_data;
		}

		bool operator==(const iterator& it) const
		{
			return _node == it._node;
		}

		bool operator!=(const iterator& it) const
		{
			return _node != it._node;
		}
	};

	//节点的实现
	template<class T>
	struct HashNode
	{
		T _data;
		HashNode<T>* _next;

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

	//K针对find和erase需要使用
	//T是针对上层unordered_set传入的是K而unordered_map传入的是pair<K, V>做出更高维度泛型的处理
	//KeyOfT是提取出vaule中的unordered_set和unordered_map中的K
	//HashFunc哈希函数
	template<class K, class T, class KeyOfT, class HashFunc>
	struct HashTable
	{
		typedef HashNode<T> Node;
		//迭代器实现中需要访问哈希表中的私有成员
		template<class K, class T, class KeyOfT, class HashFunc>
		friend class __HTIterator;
	public:
		typedef __HTIterator<K, T, KeyOfT, HashFunc> iterator;
		//析构函数需要自己实现,vector存的是内置类型,不会释放节点,只会释放表
		~HashTable()
		{
			for (int i = 0; i < _table.size(); ++i)
			{
				Node* cur = _table[i];
				while (cur)
				{
					Node* next = cur->_next;
					if (cur)
						delete cur;
					cur = next;
				}
				_table[i] = nullptr;
			}
		}

		//需要寻找第一个存储数据的位置
		iterator begin()
		{
			for (int i = 0; i < _table.size(); ++i)
			{
				Node* cur = _table[i];
				while (cur)
					if (cur)
						return iterator(cur, this);
			}

			return iterator(nullptr, this);
		}

		//末尾为nullptr
		iterator end()
		{
			return iterator(nullptr, this);
		}

		pair<iterator, bool> Insert(const T& data)
		{
			//不存入key值相同数据
			iterator pos = Find(KeyOfT()(data));
			if (pos != end())
				return make_pair(pos, false);

			//负载因子大小和表的大小一样大的时候扩容
			if (_table.size() == _n)
			{
				size_t newsize = _table.size() == 0 ? 10 : _table.size() * 2;
				vector<Node*> newtable;
				newtable.resize(newsize, nullptr);
				//遍历旧表,将旧表中的节点插入到新表中
				for (int i = 0; i < _table.size(); ++i)
				{
					Node* cur = _table[i];
					while (cur)
					{
						Node* next = cur->_next;//存储next方便更新cur
						//寻找该节点在新表中的位置
						size_t hashi = HashFunc()(KeyOfT()(cur->_data)) % newsize;
						//单链表的头插
						cur->_next = newtable[hashi];//将待插入节点的next指向待插入位置的第一个节点
						newtable[hashi] = cur;//将待插入节点插入到待插入位置
						cur = next;//更新cur
					}
					_table[i] = nullptr;//将旧表位置置空
				}
				_table.swap(newtable);//将处理好的数据交换给旧表
			}

			//经过上面处理后将数据存入
			size_t hashi = HashFunc()(KeyOfT()(data));
			hashi %= _table.size();
			//将节点头插到链表中
			Node* newnode = new Node(data);
			newnode->_next = _table[hashi];
			_table[hashi] = newnode;

			++_n;

			return make_pair(iterator(newnode, this), true);
		}

		iterator Find(const K& key)
		{
			//表空无法查找
			if (_table.size() == 0)
				return iterator(nullptr, this);

			size_t hashi = HashFunc()(key);
			hashi %= _table.size();

			//从计算到位置处开始查询该处链表中是否存在该key
			Node* cur = _table[hashi];
			while (cur)
			{
				if (KeyOfT()(cur->_data) == key)
					return iterator(cur, this);
				cur = cur->_next;
			}

			return iterator(nullptr, this);
		}

		bool Erase(const K& key)
		{
			//表为空无法删除
			if (_table.size() == 0)
				return false;

			size_t hashi = HashFunc()(key);
			hashi %= _table.size();

			//找到对应位置后进行单链表的删除
			Node* cur = _table[hashi];
			Node* prev = nullptr;
			while (cur)
			{
				//单链表的头删和普通删除
				if (KeyOfT()(cur->_data) == key)
				{
					if (prev == nullptr)
						_table[hashi] = cur->_next;
					else
						prev->_next = cur->_next;
					delete cur;
					return true;
				}
				prev = cur;
				cur = cur->_next;
			}

			return false;
		}

	private:
		vector<Node*> _table;//每个位置存储链表
		size_t _n = 0;//负载因子
	};
}

值得一提的是对于扩容,为了提高效率有将容量扩至素数这种说法,但是并没有很好的理论支持,stl3.0源码SGI版本的有采用这种方式,但是由于并没有很好的理论的支持,这里不做处理

unordered_set的封装

#pragma once
#include "HashTable.h"

namespace mystl
{
	template<class K, class HashFunc = DefaultHash<K>>
	struct Unordered_Set
	{
		//提供K
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

	public:
		typedef typename Bucket::HashTable<K, K, SetKeyOfT, HashFunc>::iterator iterator;

		iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}

		pair<iterator, bool> insert(const K& key)
		{
			return _ht.Insert(key);
		}

		iterator find(const K& key)
		{
			return _ht.Find(key);
		}

		bool erase(const K& key)
		{
			return _ht.Erase(key);
		}

	private:
		Bucket::HashTable<K, K, SetKeyOfT, HashFunc> _ht;
	};
}

全是调用的HashTable的函数,没有什么很复杂的实现

unordered_map的封装

#pragma once
#include "HashTable.h"

namespace mystl
{
	template<class K, class V, class HashFunc = DefaultHash<K>>
	struct Unordered_Map
	{
		//提供K
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};

	public:
		typedef typename Bucket::HashTable<K, pair<K, V>, MapKeyOfT, HashFunc>::iterator iterator;

		iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}

		pair<iterator, bool> Insert(const pair<K, V>& kv)
		{
			return _ht.Insert(kv);
		}

		iterator Find(const K& key)
		{
			return _ht.Find(key);
		}

		bool Erase(const K& key)
		{
			return _ht.Erase(key);
		}

        //实现operator[]
		V& operator[](const K& key)
		{
			//调用insert,不存在插入key,否则不插入key
			pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));
			return ret.first->second;//调用operator->取数据中的value
		}

	private:
		Bucket::HashTable<K, pair<K, V>, MapKeyOfT, HashFunc> _ht;
	};
}

同上,封装HashTable的函数即可,需要注意的是unordered_map重载了operator[]而operator[]的底层是调用了哈希表的insert来做处理的文章来源地址https://www.toymoban.com/news/detail-446881.html

到了这里,关于封装unordered_set&&unordered_map的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 【C++】unordered_set 和 unordered_map 使用 | 封装

    【C++】unordered_set 和 unordered_map 使用 | 封装

    unordered_map官方文档 unordered_set 官方文档 set / map与unordered_set / unordered_map 使用功能基本相同,但是两者的底层结构不同 set/map底层是红黑树 unordered_map/unordered_set 底层是 哈希表 红黑树是一种搜索二叉树,搜索二叉树又称为排序二叉树,所以 迭代器遍历是有序的 而哈希表对应的

    2024年02月06日
    浏览(12)
  • 改造哈希表,封装unordered_map和unordered_set

    改造哈希表,封装unordered_map和unordered_set

    正文开始前给大家推荐个网站,前些天发现了一个巨牛的 人工智能 学习网站, 通俗易懂,风趣幽默 ,忍不住分享一下给大家。点击跳转到网站。 unordered_map是存的是pair是K,V型的,而unordered_set是K型的,里面只存一个值,那我们如何利用一个数据结构将他们都封装出来呢?

    2024年02月03日
    浏览(9)
  • 【C++学习】哈希表的底层实现及其在unordered_set与unordered_map中的封装

    【C++学习】哈希表的底层实现及其在unordered_set与unordered_map中的封装

    🎉个人名片: 🐼作者简介:一名乐于分享在学习道路上收获的大二在校生 🙈个人主页🎉:GOTXX 🐼个人WeChat:ILXOXVJE 🐼本文由GOTXX原创,首发CSDN🎉🎉🎉 🐵系列专栏:零基础学习C语言----- 数据结构的学习之路----C++的学习之路 🐓每日一句:如果没有特别幸运,那就请特

    2024年04月16日
    浏览(21)
  • 【高阶数据结构】封装unordered_map 和 unordered_set

    【高阶数据结构】封装unordered_map 和 unordered_set

    (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是 Scort 目前状态:大三非科班啃C++中 🌍博客主页:张小姐的猫~江湖背景 快上车🚘,握好方向盘跟我有一起打天下嘞! 送给自己的一句鸡汤🤔: 🔥真正的大师永远怀着一颗学徒的心 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏 🎉🎉

    2024年02月03日
    浏览(34)
  • C++利用开散列哈希表封装unordered_set,unordered_map

    C++利用开散列哈希表封装unordered_set,unordered_map

    1.之前我们已经实现了开散列的哈希表,今天我们来用它封装unordered_set,unordered_map 2.本文的封装比利用红黑树封装set和map更加复杂 建议大家先去看我的红黑树封装set和map再来看本文 因为有很多地方跟红黑树封装set和map时是同样的思路和方法,所以本文不会太去赘述一遍 因为un

    2024年03月24日
    浏览(12)
  • 【C++】如何用一个哈希表同时封装出unordered_set与unordered_map

    【C++】如何用一个哈希表同时封装出unordered_set与unordered_map

    👀 樊梓慕: 个人主页  🎥 个人专栏: 《C语言》 《数据结构》 《蓝桥杯试题》 《LeetCode刷题笔记》 《实训项目》 《C++》 《Linux》 《算法》 🌝 每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.哈希桶源码  2.哈希表模板参数的控制 3.字符串作为键值如何映射哈希值

    2024年03月26日
    浏览(11)
  • C++STL详解(十) -- 使用哈希表封装unordered_set和unordered_map

    C++STL详解(十) -- 使用哈希表封装unordered_set和unordered_map

    因为不同容器的类型不同,如果是unordered_map,V代表一个键值对,如果unordered_set,V代表Key值,而底层哈希表并不知道上层容器所要传递的V模板参数类型,为了与哈希表的模板参数区分,我们将哈希表中的V模板参数改为T. 例如: 在哈希表中当我们使用Find函数进行查找时: 如果上层传递的

    2024年02月01日
    浏览(18)
  • 【C++】STL——用一个哈希表封装出unordered_map和unordered_set

    【C++】STL——用一个哈希表封装出unordered_map和unordered_set

    根据先前对unordered_map和unordered_set的学习,我们得知其底层是借助哈希表来实现的,接下来我们会使用上篇博客实现的开散列哈希表来模拟实现unordered_map和unordered_set,哈希表源代码链接: Hash/Hash/HashBucket.h · wei/cplusplus - 码云 - 开源中国 (gitee.com) 下面我们对哈希表进行改造,

    2023年04月18日
    浏览(15)
  • Learning C++ No.25【开散列封装unordered_set和unordered_map】

    Learning C++ No.25【开散列封装unordered_set和unordered_map】

    北京时间:2023/5/29/7:05,上星期更文一篇,且该篇博客在周三就写完了,所以充分体现,咱这个星期摆烂充分,哈哈哈!现在的内心情感没有以前那么从容了,这次摆的时间是有点久了,但本质影响不大,因为我现在还在码字,三天不学习,或者说是没有踏实学习,目前给我

    2024年02月07日
    浏览(11)
  • 从C语言到C++_31(unordered_set和unordered_map介绍+哈希桶封装)

    从C语言到C++_31(unordered_set和unordered_map介绍+哈希桶封装)

    目录 1. unordered_set和unordered_map 1.1 unordered_map 1.2 unordered_set 1.3 unordered系列写OJ题 961. 在长度 2N 的数组中找出重复 N 次的元素 - 力扣(LeetCode) 349. 两个数组的交集 - 力扣(LeetCode) 217. 存在重复元素 - 力扣(LeetCode) 884. 两句话中的不常见单词 - 力扣(LeetCode) 2. 实现unorder

    2024年02月13日
    浏览(4)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包