哈希封装unordered系列关联式容器

文章目录

    • 补档
    • HashTable迭代器
      • 基本框架
      • 具体实现
    • HashTable模板化
      • 具体实现
    • UnorderedSet封装
      • 具体实现
    • UnorderMap封装

补档

上一次我们在使用哈希函数时说,利用仿函数可以解决不知道哈希表内存的数据类型时对哈希函数也可以进行计算,但是当时只给了一个框架,一种是可以直接强制类型转换为整型的,另一种是字符串类型

对于字符串的哈希函数有很多研究,一种比较方便的做法是使用字符串的第一个字符的ASCII码值进行简单的数学运算,或者把所有的ASCII码值相加再运算,因为如果直接作为哈希地址会导致很多重复的冲突

template<>
struct HashFunc<string> {size_t operator()(const string& key) {size_t hash = 0;for (auto e : key) {hash = hash * 31 + e;}return hash;}
};

HashTable迭代器

开放定址法比较简单,这里我们主要实现连定址法

和之前一样,我们封装需要增加迭代器、和他的基本操作,还有KeyOfT,然后再遇到什么问题我们详细说

首先第一个问题是,我们要把数据类型从原来的key、value模式转化为模板,变成这种形式

	template<class T>struct HashNode {HashNode* _next;T _data;HashNode(const T& data) :_data(data),_next(nullptr){}};

然后我们就要实现哈希表的迭代器了

基本框架

	template<class K, class T, class KeyOfT, class Hash>class HashTable;template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>struct __HTIterator {typedef HashNode<T> Node;typedef __HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> Self;Node* _node;const HashTable<K, T, KeyOfT, Hash>* _pht;size_t _hashi;__HTIerator(Node* node, HashTable<K, T, KeyOfT, Hash>* pht, size_t hashi):_node(node), _pht(pht), _hashi(hashi) {}__HTIerator(Node* node, const HashTable<K, T, KeyOfT, Hash>* pht, size_t hashi):_node(node), _pht(pht), _hashi(hashi) {}Self& operator++() {}Ref operator*() {}Ptr operator->() {}bool operator!=(const Self& s) {}};

这里的迭代器因为要访问哈希表,所以需要前置声明,访问哈希表的话,我们给一个pht指向他的节点的指针,然后给一个对应的哈希地址

具体实现

		Self& operator++() {if (_node->_next != nullptr) {_node = _node->_next;}else {_hashi++;while (_hashi < _pht->_tables.size()) {if (_pht->_tables[_hashi]!=nullptr);{_node = _pht->_tables[_hashi];break;}_hashi++}if (_hashi == _pht->_tables.size()) {_node = nullptr;}}return *this;}Ref operator*() {return _node->_data;}Ptr operator->() {return &_node->_data;}bool operator!=(const Self& s) {return _node != s._node;}

这里实现++主要有一个难点,就是当一个哈希桶走完的时候,我们如何找到下一个有效的哈希桶,一种思路是当场直接计算,取到下一个表中的值,计算出对应的哈希地址,第二种是直接现场推演,只要没有走出整个表,当我们找到一个不为空的节点时,这就是++的下一个位置,如果一直走出了表,就说明到了结尾,赋值为空即可

指针,引用,不等于,这里很简单

HashTable模板化

同样的,我们实现了迭代器之后,哈希表就也需要模板化了,由于迭代器内部使用了哈希表中的数据,我们还需要给哈希表中添加迭代器友元

具体实现

	template<class K, class T, class KeyOfT, class Hash>class HashTable {typedef HashNode<T> Node;template<class K, class T, class Ref, class KeyOfT, class Hash>friend struct __HTIterator;public:typedef __HTIterator<K, T, T&, T*, KeyOfT, Hash> iterator;typedef __HTIterator<K, T, const T&, const T*, 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);}iterator begin() const {for (size_t i = 0; i < _tables.size(); i++) {if (_tables[i]) {return const_iterator(_tables[i], this, i);}}return end();}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 T& data) {Hash hf;KeyOfT kot;iterator it = Find(kot(data));if (it != end())return make_pair(it, false);if (_n == _tables.size()) {vector<Node*> newTables;newTables.resize(_tables.size() * 2, nullptr);for (size_t i = 0; i < _tables.size(); i++) {Node* cur = _tables[i];while (cur) {Node* next = cur->_next;size_t hashi = hf(kot(cur->_data)) % newTables.size();cur->_next = newTables[i];newTables[hashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newTables);}size_t hashi = hf(kot(data)) % _tables.size();Node* newnode = new Node(data);newnode->_next = _tables[hashi];_tables[hashi] = newnode;++_n;return make_pair(iterator(newnode, this, hashi), true);}iterator Find(const K& key) {Hash hf;KeyOfT kot;size_t hashi = hf(kot(key)) % _tables.size();Node* cur = _tables[hashi];while (cur) {if (kot(cur->_data) == key) {return iterator(cur, this, hashi);}cur = cur->_next;}return end();}bool Erase(cosnt K& key) {Hash hf;KeyOfT kot;size_t hashi = hf(key) % _tables.size();Node* prev = nullptr;Node* cur = _tables[hashi];while (cur) {if (kot(cur->_data) == key) {if (prev == nullptr)_tables[hashi] = cur->_next;elseprev->_next = cur->_next;delete cur;return true;}prev = cur;cur = cur->_next;}return false;}private:vector<Node*> _tables;size_t _n = 0;};

在迭代器部分我们有两个构造函数,其实是为了两个迭代器做准备,因为如果构造函数只有普通版本,在传入const迭代器时,会有权限的放大,会报错,因此我们要实现两套迭代器

然后还需要更改的是每一个T都要用KeyOfT取值才可以进行运算,稍后我们封装成map和set的时候会把哈希函数分别封装进去

UnorderedSet封装

具体实现

namespace xu {template<class K, class Hash = HashFunc<K>>class unordered_set {struct SetKeyOfT {const K& operator(const K& key) {return key;}};public:typedef typename hash_bucket::HashTable<K, K, SetKeyOfT, Hash>::const_iterator iterator;typedef typename hash_bucket::HashTable<K, K, SetKeyOfT, Hash>::const_iterator const_iterator;const_iterator begin() const {return _ht.begin();}const_iterator end() const {return _ht.end();}pair<const_iterator, bool> insert(const K& key){auto ret = _ht.Insert(key);return pair<const_iterator, bool>(const_iterator(ret.first._node, ret.first._pht, ret.first._hashi), ret.second);}iterator find(const K& key) {return _ht.Find(key);}bool erase(const K& key) {return _ht.Erase(key);}private:hash_bucket::HashTable<K, K, SetKeyOfT, Hash> _ht;};
}

这里封装倒没有什么难理解的,因为Set不能被更改,所以他的const迭代器是const迭代器,普通迭代器也是const迭代器,因此我们只需要实现const迭代器的部分,即便是普通迭代器,转换到const迭代器也只是权限的缩小,这里是支持的

比较难理解的是插入的这个返回值是什么意思,为什么不能直接返回哈希表里面的插入值呢

因为哈希表本身是支持普通迭代器和const迭代器的,而当set或者map插入时,是完全能成功的,但是在返回时,pair内部只有const迭代器,本质原因是因为普通迭代器不能用于构造const迭代器(因为编译器不认识),注意这里不是简单的类型转换,而是构造

那我们为什么不能直接像之前的红黑树一样直接改成Node*,主要还是因为哈希表的迭代器模板参数更多更加复杂,如果只是Node*是不够用的

我们知道了这个错误的产生原因,就有思路可以解决了,就是不让他自动构造,我们手动构造一个迭代器给她返回就没问题了,也就是使用auto接收插入的返回值,然后再手动构造一个const迭代器,返回,就解决了这里的报错

UnorderMap封装

namespace xu {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;}};public:typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::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);}const V& operator[](const K& key) const {pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));return ret.first->second;}V& operator[](const K& key) {pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));return ret.first->second;}iterator find(const K& key) {return _ht.Find(key);}bool erase(const K& key) {return _ht.Erase(key);}private:hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;};
}

map封装比set封装要简单一些,需要注意的是map支持方括号访问,支持方括号修改,具体原因见介绍map和set那一部分

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

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

相关文章

Android判断应用是否在前台运行

Android判断应用是否在前台运行 /*** Android判断应用是否在前台运行* 0&#xff1a;异常&#xff1b;1&#xff1a;前台&#xff1b;2&#xff1a;后台&#xff1b;3&#xff1a;未运行&#xff1b;*/private int isFrontShow(Context context) {if (context null) {ret…

基于STM32 的机场安检闸机排队系统

项目&#xff1a;基于STM32 的机场安检闸机排队系统 应用场景&#xff1a;现实中可能涉及到人数限额排队的情况下使用&#xff0c;例如安检&#xff08;不能一次性通过太多人数&#xff1a;机场、高铁站、地铁等&#xff09;&#xff0c;电梯人数限额&#xff08;一个电梯内限…

【Java那些年系列-启航篇 03】JDK、JRE和JVM之间是什么关系?

作者名称&#xff1a;纸飞机-暖阳 作者简介&#xff1a;专注于Java和大数据领域&#xff0c;致力于探索技术的边界&#xff0c;分享前沿的实践和洞见 文章专栏&#xff1a;Java那些年专栏 专栏介绍&#xff1a;本专栏涵盖了 Java SE从基础语法到面向对象编程&#xff0c;从异常…

VS2019中配置C++ OpenCV 4.5.4完整指南

⭐️我叫忆_恒心&#xff0c;一名喜欢书写博客的在读研究生&#x1f468;‍&#x1f393;。 如果觉得本文能帮到您&#xff0c;麻烦点个赞&#x1f44d;呗&#xff01; 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧&#xff0c;喜欢的小伙伴给个三…

STM32标准库ADC和DMA知识点总结

目录 前言 一、ADC模数转换器 &#xff08;1&#xff09;AD单通道 &#xff08;2&#xff09;AD多通道 二、DMA原理和应用 &#xff08;1&#xff09;DMA数据转运&#xff08;内存到内存&#xff09; &#xff08;2&#xff09;DMAAD多同道&#xff08;外设到内存&#x…

堆排序(升)

堆排序&#xff08;升&#xff09; 堆排序是一种基于二叉堆数据结构的排序算法。它的主要想是将待排序的序列构建成一个大顶堆&#xff08;或小顶&#xff09;&#xff0c;然后依次将堆顶元素与最后一个元素交换&#xff0c;并重新调整堆&#xff0c;使得剩余元素仍满足堆的性…

实习学习内容-Lua语法

Lua是一种轻量级的脚本语言&#xff0c;以其简单、灵活和高效的特点被广泛应用于嵌入式系统、游戏开发和服务器端编程中。Lua语言的设计目标是为了嵌入应用程序中&#xff0c;提供灵活的扩展和定制功能。下面&#xff0c;我将简要介绍Lua的基本语法和特点。 基本语法 变量和类…

24深圳杯数学建模挑战赛A题6页初步思路+参考论文+保姆级答疑!!!

问题1:单个残骸的精确位置定位 建立数学模型&#xff0c;分析如果要精准确定空中单个残骸发生音爆时的位置坐标&#xff08;经度、纬度、高程&#xff09;和时间&#xff0c;至少需要布置几台监测设备&#xff1f;假设某火箭一级残骸分离后&#xff0c;在落点附近布置了7台监测…

【热门话题】AI作画算法原理解析

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 AI作画算法原理解析AI作画算法概述基础原理&#xff1a;机器学习与深度学习卷积…

Bentley二次开发教程24-交互式类工具

交互式工具概念简述 本次内容主要涉及到交互式工具的使用&#xff0c;在MicroStation中&#xff0c;超过一半的功能都是以交互式工具的形式而存在的&#xff0c;因此交互式工具在MicroStation二次开发中便显得非常重要。当我们的鼠标或键盘在视图中产生交互操作时&#xff0c;…

黑马微服务课程2

课程地址&#xff1a;2024最新SpringCloud微服务开发与实战&#xff0c;java黑马商城项目微服务实战开发&#xff08;涵盖MybatisPlus、Docker、MQ、ES、Redis高级等&#xff09;_哔哩哔哩_bilibili 课程名称&#xff1a;2024最新SpringCloud微服务开发与实战&#xff0c;java…

《A More Fine-Grained Aspect-Sentiment-Opinion Triplet Extraction Task》阅读笔记

一、论文简介 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;情感分析一直是一个热门的研究主题&#xff0c;它帮助机器理解文本中的情感倾向。随着技术的进步&#xff0c;研究者们不断推动情感分析向更细粒度的方向发展&#xff0c;即从简单的正负情感判断&#…

【VueUse】重新定义状态管理在 Vue 中的体验

在 Vue 生态系统中&#xff0c;状态管理一直是开发者们关注的焦点之一。而随着 VueUse 的出现&#xff0c;我们迎来了一种全新的方式来处理状态管理&#xff0c;它让我们能够以更简单、更灵活的方式来管理应用程序的状态。 在本文中我们将深入探讨 VueUse 中与状态管理相关的内…

StrongSORT——基于DeepSORT,提高多目标跟踪的准确性和鲁棒性

1、概述 1.1 DeepSORT DeepSORT算法是在SORT基础上发展起来的一种多目标跟踪算法。SORT算法结合了目标检测器和跟踪器&#xff0c;其中跟踪器的核心是卡尔曼滤波和匈牙利算法。 卡尔曼滤波用于预测目标在下一帧的位置和状态而匈牙利算法则用于将预测状态与实际检测结果进行最…

SpringCloud基础 Consul的引入

前言 首先是为什么引入consul这个组件 我们知道微服务分为很多个模块,这里模块中相互调用,我使用硬编码的模式是不好的 比如微服务模块需要更新的时候,我们使用硬编码的方式可能需要修改很多个地方 但是使用consul之后,就引入了注册中心,我们只需要将对应的服务注册为节点 这样…

android脱壳第二发:grpc-dumpdex加修复

上一篇我写的dex脱壳&#xff0c;写到银行类型的app的dex修复问题&#xff0c;因为dex中被抽取出来的函数的code_item_off 的偏移所在的内存&#xff0c;不在dex文件范围内&#xff0c;所以需要进行一定的修复&#xff0c;然后就停止了。本来不打算接着搞得&#xff0c;但是写了…

【论文阅读】EgoPCA: A New Framework for Egocentric Hand-Object Interaction

论文主要贡献 提出一种新的框架&#xff1a;Ego-HOI recognition by Probing, Curation and Adaption (EgoPCA)。构建了全面的预训练集&#xff0c;平衡的测试集&#xff0c;以及一个包含了微调策略的baseline。 在Ego-HOI达到了SOTA&#xff0c;并且建立了有效的机制方法。 …

【后端】git与python的结合使用

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、git介绍二、git常见使用三、git与python的结合使用四、总结 前言 随着开发语言及人工智能工具的普及&#xff0c;使得越来越多的人会主动学习使用一些开发…

Python搭建http下载服务器

import http.server import socketserverPORT 8002Handler http.server.SimpleHTTPRequestHandlerwith socketserver.TCPServer(("", PORT), Handler) as httpd:print("serving at port", PORT)httpd.serve_forever()使用&#xff1a; 保存为httpserver.…

Python计算两个时间的时间差(工作笔记需要自取)

目录 专栏导读方法1&#xff1a;方法2总结 专栏导读 &#x1f338; 欢迎来到Python办公自动化专栏—Python处理办公问题&#xff0c;解放您的双手 &#x1f3f3;️‍&#x1f308; 博客主页&#xff1a;请点击——> 一晌小贪欢的博客主页求关注 &#x1f44d; 该系列文章…