从零开始手写STL库:HashTable

news2024/9/21 13:46:07

从零开始手写STL库–HashTable的实现

Gihub链接:miniSTL


文章目录

  • 从零开始手写STL库–HashTable的实现
  • HashTable是什么
  • HashTable需要包含什么函数
    • 基础成员部分
    • 基础函数部分
    • 可用函数部分
  • 其他函数
  • 总结


HashTable是什么

HashTable在STL中直接出现的情况并不多,但却是很重要的底层结构。

STL库中的unordered_set和unordered_map均由它构成。

实际上也就是大家所熟悉的哈希表,是通过哈希函数将键映射到索引的一种数据结构,一般是不允许重复键值存在的,

所以一般都用来做搜索工作。

HashTable需要包含什么函数

通常来讲HashTable需要解决哈希函数映射冲突解决迭代器等等功能。本文提供一种简化的HashTable实现。

基础成员部分

首先需要知道的是,STL库中HashTable这一数据结构的实现方式为桶数组+链表
在这里插入图片描述
当输入一个键值时,通过哈希函数计算它的索引,寻找到对应的桶,再插入该桶后面的链表中,这就是一次插入过程

所以首先需要构建基础元素,HashNode,也就是上图中每一行组成的基础元素,多个基础元素拼起来也就成了一个HashTable

template <typename Key, typename Value, typename Hash = std::hash<Key>>
class HashTable {
  class HashNode {
  public:
    Key key;
    Value value;

    explicit HashNode(const Key &key) : key(key), value() {}

    // 从Key和Value构造节点
    HashNode(const Key &key, const Value &value) : key(key), value(value) {}

    // 比较算符重载,只按照key进行比较
    bool operator==(const HashNode &other) const { return key == other.key; }

    bool operator!=(const HashNode &other) const { return key != other.key; }

    bool operator<(const HashNode &other) const { return key < other.key; }

    bool operator>(const HashNode &other) const { return key > other.key; }

    bool operator==(const Key &key_) const { return key == key_; }

    void print() const {
      std::cout << key << " "<< value << " ";
    }
  };
};

当有了基础元素,就可以将这些单独元素统一起来成为一个HashTable了

private:
  using Bucket = std::list<HashNode>; // 定义桶的类型为存储键的链表
  std::vector<Bucket> buckets;        // 存储所有桶的动态数组
  Hash hashFunction;                  // 哈希函数对象
  size_t tableSize;                   // 哈希表的大小,即桶的数量
  size_t numElements;                 // 哈希表中元素的数量

需要注意的是,哈希表需要动态地调整桶数组的大小来保持较好的性能,不然一个桶后面跟了太多的数字,会降低搜索效率

所以定义一个负载因子,当链表数量与桶数量之比大于这个因子,就进行rehash工作

float maxLoadFactor = 0.75; // 默认的最大负载因子

基础函数部分

当结构完整了,就可以开始定义函数了。

首先就是哈希映射函数

size_t hash(const Key &key) const { return hashFunction(key) % tableSize; }

用来接收键值(Key),并返回一个索引值(Index),来储存该键值

接着就是上文提到的rehash函数,与vector、deque的resize函数相同,这个函数也需要放在private中,不能随意让外界调用

  void rehash(size_t newSize) {
    std::vector<Bucket> newBuckets(newSize); // 创建新的桶数组

    for (Bucket &bucket : buckets) {      // 遍历旧桶
      for (HashNode &hashNode : bucket) { // 遍历桶中的每个键
        size_t newIndex =
            hashFunction(hashNode.key) % newSize; // 为键计算新的索引
        newBuckets[newIndex].push_back(hashNode); // 将键添加到新桶中
      }
    }
    buckets = std::move(newBuckets); // 使用move来更新,从而省去复制操作,优化性能
    tableSize = newSize;             // 更新哈希表大小
  }

接下来就是可以为外界使用的函数了

可用函数部分

1、构造函数:

  HashTable(size_t size = 10, const Hash &hashFunc = Hash())
      : buckets(size), hashFunction(hashFunc), tableSize(size), numElements(0) {
  }

这里的hash函数如果用户不指定,那就用STL库中自带的hash函数来初始化

2、插入函数
通过该函数,将键插入到哈希表中

  void insert(const Key &key, const Value &value) {
    if ((numElements + 1) > maxLoadFactor * tableSize) { // 检查是否需要重哈希
      
      if (tableSize == 0) tableSize = 1;// 防止之前进行了 clear 导致的 tableSize = 0 的情况
      rehash(tableSize * 2); // 重哈希,桶数量翻倍
    }
    size_t index = hash(key);                     // 计算键的索引
    std::list<HashNode> &bucket = buckets[index]; // 获取对应的桶
    // 如果键不在桶中,则添加到桶中
    if (std::find(bucket.begin(), bucket.end(), key) == bucket.end()) {
      bucket.push_back(HashNode(key, value));
      ++numElements; // 增加元素数量
    }
  }
  
  void insertKey(const Key &key) { insert(key, Value{}); } // 只插入键值,没有value的情况

3、移除函数
通过该函数,将哈希表中存在的值删除,当然也需要区分存在和不存在的情况,不存在的话要注意不能内存溢出

  void erase(const Key &key) {
    size_t index = hash(key);      // 计算键的索引
    auto &bucket = buckets[index]; // 获取对应的桶
    auto it = std::find(bucket.begin(), bucket.end(), key); // 查找键
    if (it != bucket.end()) {                               // 如果找到键
      bucket.erase(it); // 从桶中移除键
      numElements--;    // 减少元素数量
    }
  }

4、查找函数
通过给定的键来查找该元素是否存在,按照STL库的函数,本函数需要返回一个指针,指向找到的值,否则返回end()

  Value *find(const Key &key) {
    size_t index = hash(key);      // 计算键的索引
    auto &bucket = buckets[index]; // 获取对应的桶
    // 返回键是否在桶中
    auto ans = std::find(bucket.begin(), bucket.end(), key);
    if (ans != bucket.end()) {
      return &ans->value;
    };
    return nullptr;
  }

其他函数

  // 获取哈希表中元素的数量
  size_t size() const { return numElements; }

  // 打印哈希表中的所有元素
  void print() const {
    for (size_t i = 0; i < buckets.size(); ++i) {
      for (const HashNode &element : buckets[i]) {
        element.print();
      }
    }
    std::cout << std::endl;
  }

  // 清空哈希表
  void clear() {
    this->buckets.clear();
    this->numElements = 0;
    this->tableSize = 0;
  }

总结

哈希表需要注意底层实现是桶数组+链表,并且需要知道rehash的用处,什么时候要rehash。

另外哈希表解决冲突不止本文这种方式,桶+链表虽然实现简单,但是极端情况下查找时间会到达O(n),而且占用内存较多

还有些方法比如

线性探测:当前桶有数字了,就顺序向下,一直到空桶;
二次探测:线性探测中把线性函数变成平方项;
双重哈希:两个哈希表,一个计算出来冲突了,就用另一个计算

知道即可,一般还是用桶+链表

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

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

相关文章

宝塔部署springboot vue ruoyi前后端分离项目,分离lib、resources

1、“文件”中创建好相关项目目录,并将项目相关文件传到对应目录 例如&#xff1a;项目名称/ #项目总目录 api/ #存放jar项目的Java项目文件 manage/ #vue管理后端界面 …

计网面试题

OSI七层模型 物理层&#xff0c;数据链路层&#xff0c;网络层&#xff0c;传输层&#xff0c;会话层&#xff0c;表示层&#xff0c;应用层 TCP/IP四层模型 应用层&#xff0c;传输层&#xff0c;网络层&#xff0c;网络层接口 HTTP&#xff08;超文本传输协议&#xff09;…

C语言典型例题20

《C程序设计教程&#xff08;第四版&#xff09;——谭浩强》 例题2.8 有两个整形变量a和b&#xff0c;将他们两个的值进行互换 代码&#xff1a; //《C程序设计教程&#xff08;第四版&#xff09;——谭浩强》 //例题2.8 有两个整形变量a和b&#xff0c;将他们两个的值进行…

【通俗理解】牛顿第三定律与生命反应——超越物自在主义的探讨

【通俗理解】牛顿第三定律与生命反应——超越物自在主义的探讨 牛顿第三定律与生命的类比 你可以把牛顿第三定律比作一个“即时回应器”&#xff0c;它表明作用力和反作用力总是同时、同量且反向发生。而生命则像是一个“策略玩家”&#xff0c;它不仅回应&#xff0c;还能操纵…

flutter存储日志文件代码

如题所示&#xff0c;我需要将flutter运行得到的相关日志信息存储成文件&#xff0c;可以按时间或者文件大小&#xff0c;将日志文件拆分存储成多个日志文件。在项目中我已实现这个功能&#xff0c;因为觉得有用&#xff0c;所以单独简化出来放在一个demo中。 这里给出我整理出…

掌握分布式系统的38个核心概念

天天说分布式分布式&#xff0c;那么我们是否知道什么是分布式&#xff0c;分布式会遇到什么问题&#xff0c;有哪些理论支撑&#xff0c;有哪些经典的应对方案&#xff0c;业界是如何设计并保证分布式系统的高可用呢&#xff1f; 1. 架构设计 这一节将从一些经典的开源系统架…

day11 Stream流、File、方法递归、字符集、IO流(巨多API)

目录 1.Stream流 1.1认识Stream流 1.2Stream流处理数据的步骤 1.3获取Stream流 1.3.1获取集合的Stream流 1.3.2获取数组的Stream流 1.4Stream提供的常用方法 1.5终结、收集Stream流 1.5.1终结方法 1.5.2收集方法 2.可变参数 2.1可变参数是什么 2.2可变参数的特点和…

80.WEB渗透测试-信息收集-框架组件识别利用(4)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;79.WEB渗透测试-信息收集-框架组件识别利用&#xff08;3&#xff09;-CSDN博客 springb…

基于java的人居环境整治管理系统(源码+lw+部署文档+讲解等)

前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝20W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f447;&#x1f3fb;…

《如鸢》开通官号,女性向游戏爆款预定

今天&#xff0c;备受瞩目的沉浸式剧情卡牌手游《如鸢》正式开通了官方社媒账号并发布了玩家信。 《如鸢》由灵犀互娱倾力打造&#xff0c;游戏不仅拥有跌宕起伏的权谋剧情&#xff0c;更采用Live2D技术&#xff0c;为玩家带来沉浸式的游戏体验&#xff0c;吸引了众多玩家关注。…

基于SpringBoot+Vue的企业客户信息反馈平台(带1w+文档)

基于SpringBootVue的企业客户信息反馈平台(带1w文档) 基于SpringBootVue的企业客户信息反馈平台(带1w文档) 企业客户信息反馈平台的开发运用java技术&#xff0c;MIS的总体思想&#xff0c;以及MYSQL等技术的支持下共同完成了该平台的开发&#xff0c;实现了企业客户信息反馈管…

标准库中的string

字符串容量头文件&#xff1a;<string> string本质上就是字符顺序表; class string { private:char* str;size_t _size;size_t capacity; }; 1. string类对象的常见构造 (constructor) 函数名称 功能说明 string() &#xff08;重点&#xff09; 构造空的 string 类…

为你的世界,开一扇任意门(上篇)

一、每个侠客心中都有一扇任意门 童年时代就喜欢《多来A梦》&#xff0c;特比羡慕和渴望有一扇任意门&#xff0c;可以穿越到目的地&#xff0c;真正做到虽万里路&#xff0c;咫尺间。 随着技术及姿势的普及&#xff0c;基本有点常识的朋友都知道快捷方式&#xff0c;再略微熟…

手机三要素接口怎么对接呢?(一)

一、什么是手机三要素&#xff1f; 手机三要素又叫运营商三要素&#xff0c;运营商实名认证&#xff0c;运营商实名核验&#xff0c;手机三要素实名验证&#xff0c;手机三要素实名核验&#xff0c;每个人的称呼都不同&#xff0c;但是入参和出参是一样的。 输入姓名、身份证…

谁骑车没点心情件呀|VELO Skin Wrap把带,你的骑行显眼带!

夏日的热浪在召唤&#xff0c;是时候给你的骑行加点色彩&#xff0c;添点心情件呀&#xff01;夏季骑行&#xff0c;最怕的就是手心湿漉漉的不适&#xff0c;由此生发&#xff0c;VELO Skin Wrap系列把带&#xff0c;以天鹅绒材质精心打造&#xff0c;如同指尖上的夏日微风&…

MES系统在机床产业智能化的作用

MES系统&#xff08;Manufacturing Execution System&#xff0c;制造执行系统&#xff09;在机床产业智能化过程中发挥着至关重要的作用。以下是万界星空科技MES系统在机床产业智能化中的几个关键作用&#xff1a; 1. 实时数据采集与分析 数据采集&#xff1a;MES系统通过与生…

人工智能和计算机视觉领域国际学术会议submission

文章目录 1. AAAI 20252. CVPR 20253. ICCV 20254. IJCAI 20255. ICRA 20256. NeurIPS 20257. ACL 20258. ICLR 2025 1. AAAI 2025 人工智能促进协会&#xff08;AAAI&#xff09;是一个成立于1979年的非营利性科学组织&#xff0c;专注于深化对智能行为和思维机制的科学理解&…

暄桐好作业之《临沈周〈东庄图册〉局部》

暄桐是一间传统美学教育教室&#xff0c;创办于2011年&#xff0c;林曦是创办人和授课老师&#xff0c;教授以书法为主的传统文化和技艺&#xff0c;皆在以书法为起点&#xff0c;亲近中国传统之美&#xff0c;以实践和所得&#xff0c;滋养当下生活。      其中“暄桐好作…

八股文无用?也许是计算机大学生的重要人生指南!

大家所说的"八股文"其实指的是那些固定、标准化的面试问题和答案&#xff0c;通常涉及特定的知识点和技术概念。 博主本人也是一枚大学生&#xff0c;个人也记背过相关的八股文&#xff0c;比如计算机网络里的TCP和UDP的区别、TCP三次握手和四次挥手的具体过程等等&a…

ADS环境下的ARM汇编程序设计实验报告

ADS环境下的ARM汇编程序 一、 实验目的 1&#xff0e;了解 ARM汇编语言的基本框架,学会使用ARM的汇编语言编程。 2&#xff0e;熟悉ADS1.2下进行汇编语言程序设计的基本流程&#xff1b; 3. 了解AXD中调试功能。 二、 实验环境 硬件&#xff1a;PC机 软件&#xff1a;ADS…