【C++】哈希封装unordered_map和unordered_set

在前边的博客中我们已经实现了哈希表,我们又知道unordered_map和unordered_set就是用哈希表封装出来的,那么我们就自己来封装出它们,就跟之前用红黑树封装出set和map是一样的,我们这里使用哈希桶的版本

首先我们要用一个哈希表同时实现map和set,一个是key结构,一个是keyvalue结构,这就要求我们把哈希节点设置成模板T

map和set分别传不同的类型

下一个要实现的就是我们的迭代器,迭代器就是去遍历元素的,要找到一个元素需要哪些东西呢?我们这里可以用哈希表,第几个位置和指针来确定。但其实有了指针,我们再找到对应的值,就可以利用哈希函数计算出位置,所以只需要两个成员变量就可以了

因为我们的迭代器用到了哈希表,而哈希表有用到了迭代器,所以谁先谁后无法解决矛盾,所以我们选择在迭代器前加上哈希表的声明

并且呢,我们让哈希表作为迭代器的成员变量,多次用到了哈希表的私有成员,所以要迭代器是哈希表的友元,下面这个是写在哈希表里面的

下面还有一个点我们再实现哈希桶的时候没有说,就是我们的扩容逻辑,我们要扩容就意味着要把旧表中所有节点再计算一遍位置放到新表中,而我们这节点都是new出来的,如果delete掉再new一遍太浪费了,所以我们直接把就表中的节点挂到新表中就可以了。但还有一个关键的点就是要将旧表中的值置为空否则就会析构两遍!!!

下面是三个头文件中的所有代码

Hash_tables.h

#pragma once
#include<iostream>
#include<vector>
#include<assert.h>
using namespace std;template<class T>//哈希函数的模板
struct hashfun {size_t operator()(const T& key) {return (size_t)key;}
};
template<>//string用的多,进行了特化处理
struct hashfun<string> {size_t operator()(const string& str) {size_t hashret = 0;for (auto& e : str) {hashret = hashret * 131 + e;}return hashret;}
};
namespace hash_bucket {template<class T>//哈希节点struct HashNode {HashNode(const T&vall):val(vall),_next(nullptr){}T val;HashNode<T>* _next = nullptr;};template<class K, class V, class kov, class hashfunc>//迭代器要用到哈希表,加一个前置声明class HashTable;template<class K, class V, class kov, class hashfunc = hashfun<K>>struct HashIterator {typedef HashNode<V> Node;typedef HashTable<K, V, kov, hashfunc> HT;typedef HashIterator<K, V, kov, hashfunc> self;HashIterator(HT* htptr, Node* ptr):_htptr(htptr), _ptr(ptr) {}self& operator++() {if (_ptr->_next) {_ptr = _ptr->_next;return *this;}else {int pos = hashfunc()(kov()(_ptr->val))% _htptr->_hashvec.size();for (size_t i = pos + 1; i < _htptr->_hashvec.size(); i++) {if (_htptr->_hashvec[i]) {_ptr = _htptr->_hashvec[i];return *this;}}}_ptr = nullptr;return *this;}bool operator!=(const self& it1) {return this->_ptr != it1._ptr;}V& operator*() {if (this->_ptr == nullptr)assert(false);return this->_ptr->val;}V* operator->() {return &(this->_ptr->val);}HT* _htptr;Node* _ptr;};template<class K, class V, class kov, class hashfunc = hashfun<K>>class HashTable {typedef HashNode<V> Node;public:typedef HashIterator<K, V, kov, hashfunc> iterator;//friend struct HashIterator<K, V, kov, hashfunc>;声明友元类的两种方式template<class K, class V, class kov, class hashfunc >friend struct HashIterator;HashTable(size_t n = 10) {_hashvec.resize(n, nullptr);}~HashTable() {for (size_t i = 0; i < _hashvec.size(); i++) {Node* cur = _hashvec[i];Node* next = nullptr;while (cur) {next = cur->_next;delete cur;cur = next;}}}iterator begin() {for (size_t i = 0; i < _hashvec.size(); i++) {if (_hashvec[i])return iterator(this, _hashvec[i]);}return end();}iterator end() {return iterator(this, nullptr);}iterator find(const K& key) {kov Kov;size_t hashi = hashfunc()(key) % _hashvec.size();Node* cur = _hashvec[hashi];while (cur) {if (Kov(cur->val) == key)return iterator(this, cur);cur = cur->_next;}return end();}pair<iterator, bool> insert(const V& _kv) {kov Kov;if (find(Kov(_kv)) != end())return pair<iterator, bool>(find(Kov(_kv)), false);if (_n == _hashvec.size()) {//负载因子到一就扩容//扩容HashTable newtable(_hashvec.size() * 2);for (size_t i = 0; i < _hashvec.size(); i++) {Node* cur = _hashvec[i];Node* next = nullptr;while (cur) {next = cur->_next;size_t hashi = hashfunc()(Kov(cur->val)) % newtable._hashvec.size();cur->_next = newtable._hashvec[hashi];//头插newtable._hashvec[hashi] = cur;cur = next;}_hashvec[i] = nullptr;//原来的vector要成为空节点,防止以后析构两次}_hashvec.swap(newtable._hashvec);}size_t hashi = hashfunc()(Kov(_kv)) % _hashvec.size();Node* newnode = new Node(_kv);newnode->_next = _hashvec[hashi];_hashvec[hashi] = newnode;++_n;return make_pair(iterator(this,  newnode), true);}bool erase(const K& key) {kov Kov;size_t hashi = hashfunc()(key) % _hashvec.size();Node* prev = nullptr;Node* cur = _hashvec[hashi];while (cur && (Kov(cur->val)) != key) {prev = cur;cur = cur->_next;}if (cur == nullptr)return false;if (prev == nullptr) {_hashvec[hashi] = cur->_next;}else {prev->_next = cur->_next;}delete cur;return true;}private:size_t _n = 0;vector<Node*> _hashvec;};
}

Myunordered_map.h

#include"Hash_tables.h"
template<class K,class V>
struct mapkov {const K& operator()(const pair<K,V>&_kv) {return _kv.first;}
};template<class K,class V, class hashfunc = hashfun<K>>
class unordered_map {
public:typedef typename hash_bucket::HashTable<K, pair<K, V>, mapkov<K, V>>::iterator iterator;bool insert(const pair<K,V>& kv) {return ht.insert(kv).second;}bool erase(const K& key) {return ht.erase(key);}iterator find(const K& key) {return ht.find(key);}iterator begin() {return ht.begin();}iterator end() {return ht.end();}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<K,V>,mapkov<K,V>>ht;
};

Myunordered_set.h

#include"Hash_tables.h"
template<class K>
struct setkov {const K& operator()(const K&key) {return key;}
};template<class K,class hashfunc=hashfun<K>>
class unordered_set {
public:typedef typename hash_bucket::HashTable<K,K, setkov<K>>::iterator iterator;bool insert(const K& key) {return ht.insert(key).second;}bool erase(const K& key) {return ht.erase(key);}iterator begin() {return ht.begin();}iterator end() {return ht.end();}iterator find(const K& key) {return ht.find(key);}
private:hash_bucket::HashTable<K, K,setkov<K>>ht;
};

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

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

相关文章

01-Git 之快速入门操作本地仓库

https://learngitbranching.js.org/?localezh_CN在线练习git 1. Git 安装好Git以后, 先检查是否已经绑定了用户名和邮箱 git config --list1.1 为什么要使用版本控制&#xff1f; 从个人角度&#xff1a; 在做项目时&#xff0c;如果一点点去改代码会很乱&#xff0c;不利…

《QT实用小工具·二十五》日志重定向输出

1、概述 源码放在文章末尾 日志重定向输出&#xff0c;包含如下功能&#xff1a; 支持动态启动和停止。支持日志存储的目录。支持网络发出打印日志。支持输出日志上下文信息比如所在代码文件、行号、函数名等。支持设置日志文件大小限制&#xff0c;超过则自动分文件&#xf…

免费插件集-illustrator插件-Ai插件-批量替换链接图

文章目录 1.介绍2.安装3.通过窗口>扩展>知了插件4.功能解释5.总结 1.介绍 本文介绍一款免费插件&#xff0c;加强illustrator使用人员工作效率&#xff0c;进行批量替换链接图。首先从下载网址下载这款插件 https://download.csdn.net/download/m0_67316550/87890501&am…

ZISUOJ 数据结构-栈

题目列表&#xff1a; 问题 A: 数据结构-栈-括号匹配 思路&#xff1a; 遇到左半边括号&#xff0c;将其入栈&#xff0c;遇到右半边括号&#xff0c;则先判断栈是否为空&#xff0c;若为空&#xff0c;则匹配失败&#xff0c;若不为空&#xff0c;则再判断栈顶元素是否是与之匹…

宝塔面板部署腾讯云的域名

一、腾讯云&#xff0c;搜索我的证书&#xff0c;点击打开如图所示&#xff0c;点击下砸 二、点击宝塔的证书&#xff0c;然后下载到桌面 三、解压 四、打开宝塔&#xff0c;网站》自己的项目列表中要绑定的ssl 五、对应的文件内容复制进去&#xff0c;保存并启用证书 六、有了…

快速掌握Spring监控(Spring Boot admin)

监控 监控可视化监控平台Admin底层逻辑info 自定义端点 监控 监控的作用&#xff1a; 监控服务状态是否宕机监控服务运行指标&#xff08;内存&#xff0c;虚拟机&#xff0c;线程&#xff0c;请求等&#xff09;监控日志管理服务&#xff08;服务下线&#xff09; 监控的实…

查看 Linux 接入的 USB 设备速率是 USB2 还是 USB3

查看接入 usb 设备的速率 使用以下命令查看接入的 USB 设备速率&#xff08;每一行最后的 xxM 字样&#xff09;。插入设备前查看一次&#xff0c;插入设备后查看一次&#xff0c;对比即可定位到刚插入的设备是哪一条。 lsusb -t命令输出如下图 对照 USB 速率表 对照 USB 速…

限制登录Linux服务器的几种方式

一.第一种方法 通过修改TCP Wrappers服务访问控制来实现限制登录Linux 1.这里以sshd服务为例&#xff0c;配置完成后&#xff0c;只允许配置允许的IP才能ssh连接本机服务器&#xff0c;其他IP拒绝判断某一个基于tcp协议的服务是否支持tcp_wrapper&#xff0c;要先判断它是否支…

关注招聘 关注招聘 关注招聘

&#x1f525;关注招聘 &#x1f525;关注招聘 &#x1f525;关注招聘 &#x1f525;开源产品&#xff1a; 1.农业物联网平台开源版 2.充电桩系统开源版 3.GPU池化软件(AI人工智能训练平台/推理平台) 开源版 产品销售&#xff1a; 1.农业物联网平台企业版 2.充电桩系统企业…

TinyEMU源码分析之中断处理

TinyEMU源码分析之中断处理 1 触发中断2 查询中断2.1 查询中断使能与pending状态&#xff08;mie和mip&#xff09;2.2 查询中断总开关与委托&#xff08;mstatus和mideleg&#xff09;2.2.1 M模式2.2.2 S模式2.2.3 U模式 3 处理中断3.1 获取中断编号3.2 检查委托3.3 进入中断3…

【C++】3.类和对象(中)

一、类的6个默认成员函数 在上一篇博客中&#xff0c;我们计算了空类的大小为 1 。那么空类中真的什么东西都没有吗&#xff1f;其实不是的&#xff0c;当一个类在什么都不写的时候就会自动生成6个默认的成员函数&#xff08;用户没有写&#xff0c;但是编译器自动生成的成员函…

vue--双向数据绑定原理

Vue采用数据劫持 发布者-订阅者模式实现双向数据绑定&#xff0c;实现逻辑图如下所示&#xff1a; 数据劫持 Vue 借助Object.defineProperty()来劫持各个属性&#xff0c;这样一来属性存取过程都会被监听到 发布者-订阅者模式 主要实现三个对象&#xff1a;Observer&#…