改造哈希表,封装unordered_map和unordered_set

正文开始前给大家推荐个网站,前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。

unordered_map是存的是pair是K,V型的,而unordered_set是K型的,里面只存一个值,那我们如何利用一个数据结构将他们都封装出来呢?

我们知道哈希表我们实现的是存pair的,我们可以使用最笨的方法直接复制一份,把存pair的改为存Key的,但是我们可以参考一下大佬的做法,大佬直接把存的东西弄成一个模版参数,这个东西具体存的啥由用户来决定,用户传什么就存什么,所以改造后的哈希表的第二个类型模版参数就是我们要存的类型!

template <class T>
struct HashNode
{T _data;HashNode* _next;HashNode(const T& data): _data(data), _next(nullptr){}
};
template <class K, class V,class KeyOfT, class Hash = HashFunc<K>>
class HashTable
{typedef HashNode<V> Node;
private:KeyOfT kt;vector<Node*> _tables;size_t _n = 0;Hash hs;
};

我们可以看到V是什么类型,那么这个哈希表中存的就是什么,但是会有下一个问题,我们在取余时,不管是unordered_map还是unordered_set都是对Key取余,但是这里我们不知道他是Key还是pair,那怎么办呢?

我们可以通过仿函数解决这个问题,我们每个需要用Key计算的地方都走一层仿函数,然后unordered_set的就直接返回key就行,unordered_map则需要返回pair的first。我们会看到上面的结果多了个KeyOfT的模版,这个就是返回Key的仿函数。

unordered_map

template<class K, class V>
class unordered_map
{struct MapKOfT{const K& operator()(const pair<const K, V>& kv){return kv.first;}};private:hash_bucket::HashTable<K, pair<K,V>,MapKOfT> _ht;
};

unordered_set

template<class K>
class unordered_set
{struct SetKOfT{const K& operator()(const K& key){return key;}};private:hash_bucket::HashTable<K, K,SetKOfT> _ht;
};

至此我们最简单的框架就搭建出来了。需要注意的是所有需要用Key的地方都要走一层仿函数。
插入删除什么的直接复用哈希表的就可以,就下来主要就是实现迭代器。

迭代器

迭代器的结构应该是什么样子的?
节点的指针肯定是必须的,但是如果我们当前的桶走完了,如何++到下一个桶呢?
所以我们需要这张哈希表,用来找当前桶走完以后的下一个桶。这里不传这张哈希表也是可以的,因为我们的目的是找下一个桶,所以把哈希表中的vector传过来也是可以的。

那么迭代器如何++呢?

如果他的下一个节点是空,那么就说明这个桶走完了,我们需要找下一个桶,所以我们需要当前的位置,所以我们可以直接把当前桶的位置传过来,也可以当场计算桶的位置,这两种方法都是可以的,但是如果这张表走完了还没找到下一个桶,那就说明这张表走完了,我们直接把节点的指针改为nullptr即可。
如果它的下一个节点不为空,那直接让它等于它的next即可。

const的迭代器我们可以和之前一样,直接用两个模版参数来决定它是普通迭代器还是const迭代器。

template <class K, class V,class Ref, class Ptr, class KeyOfT, class Hash = HashFunc<K>>
struct __HTIterator
{typedef HashNode<V> Node;typedef __HTIterator<K, V,Ref,Ptr, KeyOfT, Hash> Self;Node* _node;const HashTable<K, V, KeyOfT, Hash>* _pht;size_t hashi;__HTIterator(Node* node,const HashTable<K, V, KeyOfT, Hash>* pht,size_t i): _node(node), _pht(pht), hashi(i){}Self operator++(){if (_node->_next){_node = _node->_next;}else{++hashi;while (hashi < _pht->_tables.size()){if (_pht->_tables[hashi]){_node = _pht->_tables[hashi];break;}++hashi;}if (hashi == _pht->_tables.size()){_node = nullptr;}}return *this;}bool operator!= (const Self& s){return _node != s._node;}bool operator== (const Self& s){return _node == s._node;}Ref operator*(){return _node->_data;}Ptr operator->(){return &(_node->_data);}
};

但是这里会有一个相互依赖的问题,就是哈希表需要用迭代器,迭代器需要用哈希表,如果哈希表在前面我们就需要前置声明一下迭代器,迭代器同理,我们需要在前面声明一个哈希表,但是解决完这个问题以后还存在一个问题,就是哈希表中的vector是私有成员,迭代器不能直接访问,所以我们需要把迭代器声明为哈希表的友元。
在这里插入图片描述

在这里插入图片描述
把迭代器实现好以后,接下来就是解决Key不能修改的问题。
unordeted_set和unordeted_map如何实现Key不能修改呢?
我们通过观察原码会发现unordeted_set迭代器和const迭代器都是const迭代器,它是通过这样的方式来实现的。unordeted_map是Key不能修改而Value是可以修改的,所以它的pair是pair<const K,V>它把Key设置为const,这样就能够保证Key不能修改,Value可以修改。

接下来需要实现的是unordered_map的[]重载,要实现这个重载我们就需要对哈希表的插入进行修改,它的返回值不能再是一个bool值,而是一个pair,这个pair的first是iterator迭代器,second是bool类型代表是否插入成功。改造完以后,就可以实现[]重载,但是对应容器的插入的返回值也需要变一下,[]重载主要就是存在就插入不存在就不插入,但是都会返回Val的是可以别被我们修改。

当改造完插入以后,我们会发现unordered_set的插入编译编不过,这是因为unordered_set的迭代器都是const迭代器,而哈希表的插入返回的是普通的迭代器,这里的iterator无法转化为const_iterator,所以编译错误,有两种方式可以解决,我们可以支持const迭代器转化为普通迭代器,我们也可以直接用const中的东西来构造新的普通迭代器。此时我们的封装差不多就完善了。

改造后的哈希表

namespace hash_bucket
{template <class T>struct HashNode{T _data;HashNode* _next;HashNode(const T& data): _data(data), _next(nullptr){}};template <class K, class V, class KeyOfT, class Hash>class HashTable;template <class K, class V,class Ref, class Ptr, class KeyOfT, class Hash = HashFunc<K>>struct __HTIterator{typedef HashNode<V> Node;typedef __HTIterator<K, V,Ref,Ptr, KeyOfT, Hash> Self;Node* _node;const HashTable<K, V, KeyOfT, Hash>* _pht;size_t hashi;__HTIterator(Node* node,const HashTable<K, V, KeyOfT, Hash>* pht,size_t i): _node(node), _pht(pht), hashi(i){}Self operator++(){if (_node->_next){_node = _node->_next;}else{++hashi;while (hashi < _pht->_tables.size()){if (_pht->_tables[hashi]){_node = _pht->_tables[hashi];break;}++hashi;}if (hashi == _pht->_tables.size()){_node = nullptr;}}return *this;}bool operator!= (const Self& s){return _node != s._node;}bool operator== (const Self& s){return _node == s._node;}Ref operator*(){return _node->_data;}Ptr operator->(){return &(_node->_data);}};template <class K, class V,class KeyOfT, class Hash = HashFunc<K>>class HashTable{typedef HashNode<V> Node;template <class K, class V,class Ref, class Ptr, class KeyOfT, class Hash>friend struct __HTIterator;public:typedef __HTIterator<K, V, V&, V*, KeyOfT, Hash> iterator;typedef __HTIterator<K, V, const V&,const V*,KeyOfT, Hash> const_iterator;iterator begin(){for (size_t i = 0; i < _tables.size(); i++){if (_tables[i]){return iterator(_tables[i], this, i);}}return end();}iterator end(){return iterator(nullptr, this, -1);}const_iterator begin() const{for (size_t i = 0; i < _tables.size(); i++){if (_tables[i]){return const_iterator(_tables[i], this, i);}}return end();}const_iterator end() const{return const_iterator(nullptr, this, -1);}HashTable(){_tables.resize(10);}~HashTable(){for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;delete cur;cur = next;}_tables[i] = nullptr;}}pair<iterator,bool> Insert(const V& data){iterator ret = Find(kt(data));if (ret!=end()){return make_pair(ret,false);}if (_n == _tables.size()){//需要扩容vector<Node*> newtables;newtables.resize(2 * _tables.size());for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];while (cur){Node* next = cur->_next;size_t hashi = hs(kt(cur->_data))% newtables.size();cur->_next = newtables[hashi];newtables[hashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newtables);}size_t hashi = hs(kt(data)) % _tables.size();Node* cur = new Node(data);cur->_next = _tables[hashi];_tables[hashi] = cur;_n++;return make_pair(iterator(cur,this,hashi), true);}//__HTIterator<K, V, V&, V*, KeyOfT, Hash>// __HTIterator<K, V,Ref,Ptr, KeyOfT, Hash>iterator Find(const K& k){size_t hashi = hs(k) % _tables.size();Node* cur = _tables[hashi];while (cur){if (kt(cur->_data) == k){return iterator(cur,this,hashi);}cur = cur->_next;}return end();}bool Erase(const K& k){size_t hashi = hs(k) % _tables.size();Node* cur = _tables[hashi];Node* prev = nullptr;while (cur){if (cur->_kv.first == k){if (prev==nullptr){_tables[hashi] = cur->_next;}else{prev->_next = cur->_next;}delete cur;return true;}cur = cur->_next;}return false;}private:KeyOfT kt;vector<Node*> _tables;size_t _n = 0;Hash hs;};
}

封装的unordered_map

template<class K, class V>class unordered_map{struct MapKOfT{const K& operator()(const pair<const K, V>& kv){return kv.first;}};public:typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKOfT>::iterator iterator;typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKOfT>::const_iterator const_iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}const_iterator begin() const{return _ht.begin();}const_iterator end() const{return _ht.end();}pair<iterator, bool> insert(const pair<K, V>& kv){return _ht.Insert(kv);}V& operator[](const K& key){pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));return ret.first->second;}private:hash_bucket::HashTable<K, pair<const K,V>,MapKOfT> _ht;};

封装的unordered_set

template<class K>
class unordered_set
{struct SetKOfT{const K& operator()(const K& key){return key;}};
public:typedef typename hash_bucket::HashTable<K, K, SetKOfT>::const_iterator iterator;typedef typename hash_bucket::HashTable<K, K, SetKOfT>::const_iterator const_iterator;iterator begin() const {return _ht.begin();}iterator end() const{return _ht.end();}pair<iterator, bool> insert(const K& key){auto ret = _ht.Insert(key);return make_pair(iterator(ret.first._node, ret.first._pht,ret.first.hashi), ret.second);}private:hash_bucket::HashTable<K, K,SetKOfT> _ht;
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/277379.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt容器QScrollArea小部件的滚动视图

​# QScrollArea 平台:linux、windows、mac皆可,Qt版本:Qt5.14.2 QScrollArea是Qt框架中用于提供可滚动视图区域的小部件。它通常被用来包含一个较大的内容区域,并且可以在其中嵌入其他小部件。下面是一些常用的QScrollArea函数: 1. `setWidget(QWidget *widget)`: 设置在…

mysql CREATE DATABASE

DROP DATABASE IF EXISTS zengwenfeng;CREATE DATABASE zengwenfeng DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS 0;USE zengwenfeng; 脚本天天少这些&#xff0c;天天找这段&#xff01;

Leetcode—118.杨辉三角【简单】

2023每日刷题&#xff08;六十&#xff09; Leetcode—118.杨辉三角 实现代码 class Solution { public:vector<vector<int>> generate(int numRows) {vector<vector<int>> ans(numRows);for(int i 0; i < numRows; i) {ans[i].resize(i 1);ans…

从 0 开始创建 SpringBoot 项目

从 0 开始创建 SpringBoot 项目 从 0 开始创建 SpringBoot 项目环境准备创建项目项目目录结构及说明编写代码参考 从 0 开始创建 SpringBoot 项目 环境准备 操作系统&#xff1a;Windows 10IDE&#xff1a;IntelliJ IDEA 2023.3.1Java 版本&#xff1a;jdk1.8 工具网盘链接&…

ChatGPT使用:一个发包机器人的提示词

发包机器人&#xff1a; 设想&#xff1a;目前项目组有n条打包线会输出多个包&#xff0c;用户想获取最新的包是比较困难的&#xff0c;难点在于 1. 分支多&#xff1a;trunk&#xff0c;release&#xff0c;outer等&#xff0c;至少有3个分支&#xff1b; 2. 多平台&#x…

Oracle MongoDB

听课的时候第一次碰到&#xff0c;可以了解一下吧&#xff0c;就直接开了墨者学院的靶场 #oracle数据库 Oracle数据库注入全方位利用 - 先知社区 这篇写的真的很好 1.判断注入点 当时找了半天没找到 看样子是找到了&#xff0c;测试一下看看 id1 and 11 时没有报错 2.判断字段…

2024中国国际大数据产业博览会年度主题征集公告

2024中国国际大数据产业博览会年度主题征集公告 中国国际大数据产业博览会&#xff08;以下简称数博会&#xff09;&#xff0c;是全球首个以大数据为主题的国家级博览会&#xff0c;由国家发展和改革委员会、工业和信息化部、国家互联网信息办公室和贵州省人民政府共同主办&am…

SQL错题集4

1.注意格式 %Y是指date的年&#xff0c;%m是指date的月 %Y-%m ’ 即为2004-01 2.查询在2025-10-15以后&#xff0c;同一个用户下单1个以上状态为购买成功的C课程或Java课程或Python课程的user_id C或Java或Python --> 缩写 in ( C,Java,Python ) in ( ) 含义为 或or 3. ca…

[渗透测试学习] Codify - HackTheBox

首先nmap扫描端口 nmap -sV -sC -p- -v --min-rate 1000 10.10.11.239扫出来三个端口&#xff0c;22端口为ssh服务&#xff0c;80端口有http服务&#xff0c;3000端口为nodejs框架 尝试访问下80端口&#xff0c;发现页面重定向 将该域名添加到hosts里 sudo vim /etc/hosts 成…

解决nuxt3生产打包以后运行服务器报错: BScroll.use is not a function

现象&#xff1a; 这个问题在开发环境不存在&#xff0c;但是在打包后的生产环境就会有这个问题 原因分析&#xff1a;从表象上看是better-scoll组件库编译似乎有问题&#xff0c;但真正的原因是better-scroll所依赖的dom元素对象不存在, 这样就导致找不到它对应的api,或者说a…

【Java 集合】ConcurrentLinkedQueue

在日常中, 我们用到的数据结构有很多: 数组, 链表, 树等, 而在这些结构中, 还有一个叫做队列的存在。 和其他的集合相同, Java 原生提供了不同的实现。 而如果我们需要一个线程安全的队列的话, 可以基于实际的场景进行选择, 比如基于数组实现同时操作上会阻塞的 ArrayBlockingQ…

Domino万物可订阅

大家好&#xff0c;才是真的好。 如果你还不知道什么是RSS&#xff0c;从V站截图一份放到这里供大家参考&#xff1a; 其实&#xff0c;Domino上也可以很简单地发布RSS站点&#xff0c;以供内部或外部用户订阅。 前面其实我们说了不少关于Notes客户端的RSS订阅功能&#xff…