C++数据结构之:哈希表Hash

摘要:

   it人员无论是使用哪种高级语言开发东东,想要更高效有层次的开发程序的话都躲不开三件套:数据结构,算法和设计模式。数据结构是相互之间存在一种或多种特定关系的数据元素的集合,即带“结构”的数据元素的集合,“结构”就是指数据元素之间存在的关系,分为逻辑结构和存储结构。

   此系列专注讲解数据结构数组、链表、队列、栈、树、哈希表、图,通过介绍概念以及提及一些可能适用的场景,并以C++代码简易实现,多方面认识数据结构,最后为避免重复造轮子会浅提对应的STL容器。本文介绍的是哈希表Hash。

(开发环境:VScode,C++17)

关键词C++数据结构哈希表Hash

声明:本文作者原创,转载请附上文章出处与本文链接。

(文章目录:)

文章目录

      • 摘要:
      • 正文:
        • 介绍:
          • 特性:
          • 应用:
        • 代码实现:
        • 对应STL:
      • 推荐阅读

正文:

介绍:

   哈希表(Hash table,也叫散列表)是一种根据关键码值(Key value)而直接进行访问的数据结构。它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做哈希函数,存放记录的数组叫做哈希表。

在这里插入图片描述

   从图中可以看出,左边是个数组,数组的每个成员包括一个指针,指向一个链表的头。我们根据元素的一些特征把元素分配到不同的链表中去,也是根据这些特征,找到正确的链表,再从链表中找出这个元素。

   哈希表的主要优点是查找速度快,其时间复杂度通常接近O(1)。然而,哈希表也存在一些缺点,如空间利用率可能不高,以及处理冲突,当两个或多个关键字具有相同的散列地址时,此情况就叫做哈希冲突,哈希冲突几乎是无法避免的,解决哈希冲突的方法主要有两种,一种是开放寻址法,一种是链表法(哈希桶,也即是图中的结构法)。

特性:
  • 快速查找:哈希表通过散列函数将关键字映射到表中的位置,从而实现常数时间复杂度的查找操作(平均情况下为O(1))。
  • 无序性:哈希表中的元素是无序的,它们按照散列函数计算出的地址进行存储。因此,哈希表不支持直接按照元素的顺序进行遍历。
  • 动态调整:哈希表的大小可以根据需要进行动态调整,以平衡空间利用率和查找效率。
应用:
  • 缓存系统:哈希表在缓存系统中被广泛使用,如CPU缓存、数据库查询缓存、Web页面缓存等。通过将数据存储在哈希表中,可以快速查找和访问缓存中的数据,从而提高系统的性能。
  • 数据库索引:哈希表可以用于构建数据库索引,加速数据的查找速度。在数据库系统中,哈希索引通常用于等值查询和范围查询。
  • URL路由:在Web框架中,哈希表可以用于实现URL路由。通过将URL路径映射到对应的处理函数或控制器,可以快速定位到处理请求的代码。
  • 网络协议:在网络协议中,哈希表可以用于实现路由表、ARP缓存等功能。这些功能需要快速查找和匹配网络地址,哈希表提供了高效的解决方案。
代码实现:
#ifndef CHASH_H
#define CHASH_H
#include <iostream>
#include <vector>  
#include <list>  
#include <functional> 
#include <string>using namespace std;
// 假设我们使用一个简单的整数键和一个字符串值  
using KeyType = string;  
using ValueType = string;  // 哈希函数,std::hash 为标准类型(如 int、std::string、std::pair 等)提供了默认的哈希函数
// 对于更复杂的键类型(如自定义类型)需要实现一个更复杂的哈希函数。
struct HashFunc {  size_t operator()(const KeyType& key) const {  return hash<KeyType>{}(key);  }
}; // 哈希表节点  
template <class K, class V>
class HashNode
{
public:K key;V value;HashNode* next;public:HashNode(K k, V v) : key(k), value(v), next(nullptr) {}private:// 禁止拷贝、赋值HashNode(const HashNode<K, V>& node);HashNode& operator=(const HashNode<K, V>& node);
};  // 哈希表实现  
template <class K, class V, class HF = HashFunc>  
class CHashTable 
{  
private:  vector<HashNode<K, V>*> buckets;  HF hashFunc;  size_t bucketSize;  public:  CHashTable(size_t size) : buckets(size, nullptr), hashFunc(), bucketSize(size) {}  ~CHashTable();                          // 析构函数(用于释放内存)void insert(K key, V value);            // 插入键值对V find(K key, V defaultValue = V());    // 查找键对应的值(如果不存在则返回默认值)  bool remove(K key);                     // 移除键值对void print();                           // 打印哈希表// 可添加动态调整大小(即当哈希表变满时增加桶的数量)};template <class K, class V, class HF>
inline CHashTable<K, V, HF>::~CHashTable()
{for (size_t i = 0; i < bucketSize; ++i){HashNode<K, V>* current = buckets[i];while (current != nullptr) {HashNode<K, V>* toDelete = current;current = current->next;delete toDelete;}}
}template <class K, class V, class HF>
inline void CHashTable<K, V, HF>::insert(K key, V value)
{size_t index = hashFunc(key) % bucketSize;// 处理冲突(这里使用链地址法)HashNode<K, V>* newNode = new HashNode<K, V>(key, value);if (buckets[index] == nullptr) {buckets[index] = newNode;} else {HashNode<K, V>* current = buckets[index];while (current->next != nullptr) {current = current->next;}current->next = newNode;}
}template <class K, class V, class HF>
inline V CHashTable<K, V, HF>::find(K key, V defaultValue)
{size_t index = hashFunc(key) % bucketSize;HashNode<K, V>* current = buckets[index];while (current != nullptr) {if (current->key == key) {return current->value;}current = current->next;}return defaultValue;
}template <class K, class V, class HF>
inline bool CHashTable<K, V, HF>::remove(K key)
{size_t index = hashFunc(key) % bucketSize;HashNode<K, V>* current = buckets[index];HashNode<K, V>* currentAfter = current->next;while (current != nullptr) {if (current->key == key) {delete current;current = nullptr;buckets[index] = currentAfter;return true;}current = current->next;currentAfter = current->next;}return false;
}template <class K, class V, class HF>
inline void CHashTable<K, V, HF>::print()
{for (size_t i = 0; i < bucketSize; ++i){HashNode<K, V>* current = buckets[i];while (current != nullptr) {HashNode<K, V>* toDelete = current;current = current->next;cout << toDelete->value << " ";}}cout << endl;
}#endif // CHASH_H
#include "chash.h"
using namespace std;int main(int argc, char**argv) {CHashTable<KeyType, ValueType> ht(10); // 创建一个大小为10的哈希表ht.insert("1", "one");ht.insert("2", "two");ht.insert("11", "eleven");ht.insert("2", "two3");cout << ht.find("2") << endl;ht.print();ht.remove("2");cout << ht.find("1") << endl;cout << ht.find("2") << endl;cout << ht.find("11") << endl;ht.print();return 0;
}

在这里插入图片描述

对应STL:

   unordered_set,unordered_multiset,unordered_map,multimap这四种容器的共同点是:底层使用了哈希表,容器中的元素是一个无序的序列。

   网络上有对 map VS unordered_map 效率对比的测试,通常 map 增删元素的效率更高,unordered_map 访问元素的效率更高,另外,unordered_map 内存占用更高,因为底层的哈希表需要预分配足量的空间。其它 xxxunordered_xxx 区别也一样。

   unordered_xxx 容器更适用于增删操作不多,但需要频繁访问,且内存资源充足的场合。

基于哈希表的集合

  • unordered_set

    容器属性:关联容器**,**无序,元素自身即key,元素有唯一性,使用内存分配器动态管理内存,可以自由的插入和删除元素。

  • unordered_multiset

    容器属性:关联容器,无序,元素自身即key,允许不同元素值相同,使用内存分配器动态管理内存,是对 unordered_set 的简单拓展。

基于哈希表的映射

  • unordered_map

    容器属性:关联容器,无序,元素类型<key, value>,key是唯一的,使用内存分配器动态管理内存。

  • unordered_multimap

    容器属性:关联容器,无序,元素类型<key, value>,允许不同元素key相同,使用内存分配器管理内存,unordered_multimap 是对 unordered_map 的拓展。

推荐阅读

C/C++专栏:https://blog.csdn.net/weixin_45068267/category_12268204.html
(内含其它数据结构及对应STL容器使用)

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

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

相关文章

鸿蒙开发 之 ArkUI路由

1.页面路由 页面路由是指在应用程序中实现不同页面之间的跳转和数据传递 比如说你打开一个app&#xff0c;首先进入的是登陆页&#xff0c;首页&#xff0c;列表搜索页&#xff0c;详情页&#xff0c;你打开几个页面都会存储在页面栈里&#xff0c;页面栈的最大容量上限为32个&…

在Oracle VM virtual box 中复制 CentOS 7虚拟机更改IP地址的操作

最近玩Redis主从复制的时候&#xff0c;我装了一个虚拟机&#xff0c;但主从复制需要准备3个虚拟机&#xff0c;这个时候&#xff0c;我又不想一个一个去装&#xff0c;我看到Oracle VM virtual box提供了一个虚拟机复制操作&#xff0c;于是就用了一下这个功能&#xff0c;发现…

云原生时代:从 Jenkins 到 Argo Workflows,构建高效 CI Pipeline

作者&#xff1a;蔡靖 Argo Workflows Argo Workflows [ 1] 是用于在 Kubernetes 上编排 Job 的开源的云原生工作流引擎。可以轻松自动化和管理 Kubernetes 上的复杂工作流程。适用于各种场景&#xff0c;包括定时任务、机器学习、ETL 和数据分析、模型训练、数据流 pipline、…

【成品设计】基于STC15F104W的互补PWM输出器

《基于STC15F104W的互补PWM输出器》 1.所需器件&#xff1a; (1)单片机&#xff1a;STC15F104W。 ①最小系统板链接&#xff1a;【淘宝】https://m.tb.cn/h.5WnLl9X?tkqSGrdCWm0PW「STC15F104W STC15W204S单片机模块 系统板 核心板 学习板 开发板」点击链接直接打开 或者 淘宝…

HCIP-Datacom-ARST自选题库__多种协议简答【11道题】

1.BGP/MPLSIP VPN的典型组网场景如图所示&#xff0c;PE1和PE2通过LoopbackO建立MP-IBGP&#xff0c;PE1和PE2之间只传递VPN路由&#xff0c;其中PE1BGP进程的部分配置已在图中标出&#xff0c;则编号为0的命令不是必须的。(填写阿拉伯数字) 3 2.在如图所示的Hub&amp;Spok…

【Java】数据加密

目录 数据加密介绍使用场景密码学历史古代密码学凯撒密码例子特点 维吉尼亚密码原理例子特点 现代密码学介绍 现代密码学的加密算法分类哈希算法优点缺点代码示例【封装写法】 对称加密算法对称加密算法的加密过程解密过程对称加密算法的优点&#xff1a;对称加密算法的缺点&am…

【初识Objective-C】

Objective-C学习 什么是OCOC的特性OC跑的第一个程序helloworld OC的一些基础知识标识符OC关键字数据类型字符型c字符串为什么NSString类型定义时前面要加和普通的c对象有什么区别 一些基础知识if语句switch语句三种循坏语句for循环&#xff1a;用于固定次数的循环while循环&…

低功耗,低噪声 CMOS 轨到轨输入输出运算放大器

产品简述 MS6001/2/4 运算放大器具有极低功耗&#xff0c;轨到轨输入输出&#xff0c;低 的输入电压和低的电流噪声。具体表现在可工作在幅度为 1.8V 到 5V 的单电源或者双电源条件&#xff0c;低功耗和低噪声使得 MS6001/2/4 能够用在可移动设备上&#xff0c;输入输…

低代码/无代码可以降低程序员哪些门槛

低代码/无代码开发平台是一种新兴的软件开发模式&#xff0c;它通过图形化界面、拖拽式操作等方式&#xff0c;快速构建应用程序&#xff0c;从而降低了开发者的准入门槛。这种模式对程序员来说&#xff0c;不仅可以提高开发效率&#xff0c;还可以在某些情况下促进业务人员成为…

目标检测数据集 - 打架检测数据集下载「包含VOC、COCO、YOLO三种格式」

数据集介绍&#xff1a;打架检测数据集&#xff0c;真实监控场景高质量打架图片数据&#xff0c;涉及场景丰富&#xff0c;比如街道场景打架数据、酒吧场景打架数据、商店场景打架数据、公交车场景打架数据、监狱场景打架数据、空旷地打架数据、两人打架数据、多人群殴数据等。…

面试官:如何实现大文件切片上传?

公众号&#xff1a;程序员白特&#xff0c;关注我&#xff0c;每天进步一点点~ 前端上传文件很大时,会出现各种问题,比如连接超时了,网断了,都会导致上传失败,这个时候就需要将文件切片上传,下面我们就来学习一下如何使用vue实现大文件切片上传吧 大文件为什么要切片上传 前端…

【SpringBoot】SpringBoot同时可以处理多少请求

目录 问题Web三大容器三者区别TomcatJetty小结 最大连接数和最大等待数同时处理请求数拓展&#xff1a;设置Web容器设置容器为Jetty设置容器为Undertow 问题 之前看到过一个面试题&#xff1a;SpringBoot同时可以处理多少请求&#xff1f; 准确的来说&#xff0c;Spring Boot…

pycharm绘图时中英文不能同时出现 中文出现小框框的问题解决

# 设置字体为微软雅黑&#xff0c;正确显示负号 plt.rcParams[font.sans-serif] [Microsoft YaHei] plt.rcParams[axes.unicode_minus] False

Python 组合序号

import pandas as pd # 创建一个示例数据框 data { group: [A, A, A, B, B, C, C, C, C], value: [3, 1, 2, 5, 4, 6, 9, 7, 8] } df pd.DataFrame(data) # 先按group分组&#xff0c;再按value列升序排序 df_sorted_asc df.sort_values(by[group, value]) # 使…

如何使用vsCode打开intel D435i深度相机

一、下载并安装相机SDK文件 1.SDK下载地址&#xff1a; Release Intel RealSense™ SDK 2.0 (v2.54.2) IntelRealSense/librealsense GitHub 2.下载后&#xff0c;双击即可安装 3.环境配置 1&#xff09;window的开始菜单&#xff0c;搜索环境变量&#xff0c;选择编辑系…

LitCTF2024部分wp

litctf wp 第一次ak了web和misc&#xff0c;非常激动&#xff0c;感谢lictf给我这个机会 最终成果 全靠队里的密码逆向✌带飞。一个人就砍了近一半的分数 这里是我们队的wp web exx 题目名反过来就是xxe&#xff0c;考察xxe&#xff0c;查看登录的数据包 发现传的就是xml…

机器学习中的泛化与适应:深入理解域泛化、域适应、少样本学习、零样本学习、开放世界识别与开放词汇识别

机器学习中的泛化与适应&#xff1a;深入理解域泛化、域适应、少样本学习、零样本学习、开放世界识别与开放词汇识别 &#x1f60e; 作者介绍&#xff1a;我是程序员行者孙&#xff0c;一个热爱分享技术的制能工人。计算机本硕&#xff0c;人工制能研究生。公众号&#xff1a;A…

Java Web学习笔记8——表单

表单标签&#xff1a; 场景&#xff1a;在网页中主要负责数据采集功能&#xff0c;如注册、登录等数据采集。 标签&#xff1a;<form> 表单项、表单元素&#xff1a; 不同类型的input元素、下拉列表、文本域等。 <input>: 定义表单项、通过type形式控制输入形…

深入解读Prometheus Adapter:云原生监控的核心组件

一、引言 Prometheus Adapter的背景与重要性 在现代的云原生架构中&#xff0c;微服务和容器化技术得到了广泛的应用。这些技术带来了系统灵活性和扩展性的提升&#xff0c;但同时也增加了系统监控和管理的复杂度。Prometheus作为一款开源的监控系统&#xff0c;因其强大的指标…

有关Qt的调用其他cpp文件出现的小问题

一开始出现了运行mainwindow文件过程调用其他cpp文件&#xff0c;而导致运行的ui界面卡住&#xff0c;后来发现在两个cpp文件中都进行了界面的初始化而导致界面的某些控件二次使用&#xff0c;所以会卡住。 ui->setupUi(this); 在Qt框架中&#xff0c;ui->setupUi(this)…