当前位置: 首页 > news >正文

unordered_map和set

前言:本篇文章继续分享新的容器unordered_map和set。前边我们分享过map和set,其底层为红黑树,而unordered_map和set的底层则为哈希表,因此在unordered_map和set的实现中,我们可以效仿许多在map和set的中就分享过的一些知识,所以在本篇文章中,就不对分享过的知识进行重复讲解。

而unordered_map和set与map和set的区别,即为红黑树和哈希表的区别


目录

一.修改hash

二.迭代器

三.完整代码

1.unordered_map

2.unordered_set

四.总结


一.修改hash

我们所实现的普通哈希表肯定是无法直接拿来作为unordered_map和set的底层代码的,所以我们需要对其进行优化修改,完整代码如下:

template<class K>
struct HashFunc
{size_t operator()(const K& key){return (size_t)key;}
};struct HashStringFunc
{size_t operator()(const string& s){size_t hash = 0;for (auto e : s){hash += e;}return hash;}
};namespace hash_bucket
{template<class T>struct HashNode{T _data;HashNode<T>* _next;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 KeyOfT, class Hash>struct __Iterator{typedef HashNode<T> Node;typedef __Iterator<K, T, KeyOfT, Hash> Self;Node* _node;HashTable<K, T, KeyOfT, Hash>* _pht;__Iterator(Node* node, HashTable<K, T, KeyOfT, Hash>* pht):_node(node),_pht(pht){}T& operator*(){return _node->_data;}T* operator->(){return &_node->_data;}Self& operator++(){if (_node->_next)//当前桶没走完,找到下一个节点_node = _node->_next;else{//当前桶走完了,找下一个不为空的桶的第一个节点KeyOfT kot;Hash hs;size_t i = hs(kot(_node->_data)) % _pht->_tables.size();i++;for (; i < _pht->_tables.size(); i++){if (_pht->_tables[i])break;}if (i == _pht->_tables.size())//所有桶都走完了,置nullptr_node = nullptr;else_node = _pht->_tables[i];}return *this;}bool operator!=(const Self& s){return _node != s._node;}};template<class K, class T, class KeyOfT, class Hash>class HashTable{typedef HashNode<T> Node;public://友元template<class K, class T, class KeyOfT, class Hash>friend struct __Iterator;typedef __Iterator<K, T, KeyOfT, Hash> iterator;iterator begin(){for (size_t i = 0; i < _tables.size(); i++){Node* cur = _tables[i];if (cur){return iterator(cur, this);}}return end();}iterator end(){return iterator(nullptr, this);}HashTable(){_tables.resize(10, nullptr);_n = 0;}~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){KeyOfT kot;Hash hs;//判断是否存在iterator it = Find(kot(data));if (it != end())return make_pair(it,false);//扩容if (_n == _tables.size()){vector<Node*> newtables(_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 = hs(kot(cur->_data)) % newtables.size();cur->_next = newtables[hashi];newtables[hashi] = cur;cur = next;}_tables[i] = nullptr;}_tables.swap(newtables);}size_t hashi = hs(kot(data)) % _tables.size();Node* newnode = new Node(data);//头插newnode->_next = _tables[hashi];_tables[hashi] = newnode;++_n;return make_pair(iterator(newnode,this), false);}//寻找iterator Find(const K& key){KeyOfT kot;Hash hs;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];while (cur){if (kot(cur->_data) == key)return iterator(cur,this);cur = cur->_next;}return end();}bool Erase(const K& key){KeyOfT kot;Hash hs;size_t hashi = hs(key) % _tables.size();Node* cur = _tables[hashi];Node* prev = nullptr;while (cur){if (kot(cur->_data) == key){//删除的是第一个节点if (prev == nullptr){_tables[hashi] = cur->_next;}else{prev->_next = cur->_next;}delete cur;}else{prev = cur;cur = cur->_next;}}return false;}private:vector<Node*> _tables;//指针数组size_t _n;};}

这其中包括只对key进行操作的修改,以及当插入的元素为string型时对其进行的特殊处理。都是我们之前所分享过的知识。下面我们重点来看迭代器


二.迭代器

先来看整体结构:

template<class K, class T, class KeyOfT, class Hash>struct __Iterator{typedef HashNode<T> Node;typedef __Iterator<K, T, KeyOfT, Hash> Self;Node* _node;HashTable<K, T, KeyOfT, Hash>* _pht;__Iterator(Node* node, HashTable<K, T, KeyOfT, Hash>* pht):_node(node),_pht(pht){}T& operator*(){return _node->_data;}T* operator->(){return &_node->_data;}Self& operator++(){if (_node->_next)//当前桶没走完,找到下一个节点_node = _node->_next;else{//当前桶走完了,找下一个不为空的桶的第一个节点KeyOfT kot;Hash hs;size_t i = hs(kot(_node->_data)) % _pht->_tables.size();i++;for (; i < _pht->_tables.size(); i++){if (_pht->_tables[i])break;}if (i == _pht->_tables.size())//所有桶都走完了,置nullptr_node = nullptr;else_node = _pht->_tables[i];}return *this;}bool operator!=(const Self& s){return _node != s._node;}};

其中最为重要的莫过于++运算符的重载,因为我们的哈希表是闭散列的哈希桶,所以是以指针数组作为底层。

在执行++操作时,需要先判断当前节点的next是否存在,存在就直接为next节点,不存在就说明当前节点已经是其所属的桶里的最后一个节点,那么接下来的操作就是去寻找下一个不为空的桶的第一个节点

此时我们需要先计算出当前节点在数组中的位置,然后通过循环判断其后边的位置是否存在桶如果存在,就返回新桶的第一个节点,不存在,就说明所有的桶都走完了,此时返回空指针nullptr


三.完整代码

1.unordered_map

#include"hash.h"
namespace Myunordered_map
{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();}V& operator[](const K& key){pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;}pair<iterator, bool> insert(const pair<K, V>& kv){return _ht.Insert(kv);}private:hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;};
}

2.unordered_set

#include"hash.h"
namespace Myunordered_set
{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, const K, SetKeyOfT, Hash>::iterator iterator;iterator begin(){return _ht.begin();}iterator end(){return _ht.end();}pair<iterator, bool> insert(const K& key){return _ht.Insert(key);}private:hash_bucket::HashTable<K, const K, SetKeyOfT, Hash> _ht;};
}

四.总结

关于unordered_map和set就分享这么多,通过前边的知识的分享和掌握,unordered_map和set的实现也是如鱼得水。

喜欢本篇文章的小伙伴记得一键三连,我们下期再见!

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • VPS拨号服务器:独享的高效与安全
  • MMII 的多模态医学图像交互框架:更直观地理解人体解剖结构和疾病
  • 电脑拼图软件有哪些?盘点7种简单好用电脑拼图软件
  • 张量分解(3)——CP分解
  • Kindling-OriginX 在快手 Staging 环境的异常诊断效果分享
  • 如何切换手机的ip地址
  • 怎么搭建微信商城
  • 【代码随想录算法训练营第六十四天|卡码网47.参加科学大会、94.城市间货物运输I】
  • 少年时期的黑客天才
  • 电焰灶:烹饪性能的深度剖析
  • Spring中常见知识点及使用
  • leetcode300:最长递增子序列
  • 如何使用nestjs生成一个新的控制器
  • 【区块链 + 智慧政务】一体化政务数据底座平台 | FISCO BCOS应用案例
  • 算法——二分法
  • (三)从jvm层面了解线程的启动和停止
  • bootstrap创建登录注册页面
  • iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
  • Java精华积累:初学者都应该搞懂的问题
  • Java-详解HashMap
  • Linux快速复制或删除大量小文件
  • nginx 负载服务器优化
  • Node项目之评分系统(二)- 数据库设计
  • npx命令介绍
  • Octave 入门
  • puppeteer stop redirect 的正确姿势及 net::ERR_FAILED 的解决
  • session共享问题解决方案
  • vue--为什么data属性必须是一个函数
  • webpack入门学习手记(二)
  • 前端路由实现-history
  • 浅谈web中前端模板引擎的使用
  • 使用 Docker 部署 Spring Boot项目
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 携程小程序初体验
  • ​​​​​​​​​​​​​​汽车网络信息安全分析方法论
  • ​2020 年大前端技术趋势解读
  • ​批处理文件中的errorlevel用法
  • #include
  • #Linux(Source Insight安装及工程建立)
  • #pragma data_seg 共享数据区(转)
  • #图像处理
  • (2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (JS基础)String 类型
  • (python)数据结构---字典
  • (附源码)springboot 智能停车场系统 毕业设计065415
  • (附源码)springboot炼糖厂地磅全自动控制系统 毕业设计 341357
  • (附源码)springboot青少年公共卫生教育平台 毕业设计 643214
  • (剑指Offer)面试题34:丑数
  • (论文阅读31/100)Stacked hourglass networks for human pose estimation
  • (数据结构)顺序表的定义
  • (转)setTimeout 和 setInterval 的区别
  • (转)项目管理杂谈-我所期望的新人
  • (转载)hibernate缓存
  • (最简单,详细,直接上手)uniapp/vue中英文多语言切换
  • .“空心村”成因分析及解决对策122344