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

C++_HELLO算法_哈希表的简单实现

6.1.2   哈希表简单实现¶

我们先考虑最简单的情况,仅用一个数组来实现哈希表。在哈希表中,我们将数组中的每个空位称为桶(bucket),每个桶可存储一个键值对。因此,查询操作就是找到 key 对应的桶,并在桶中获取 value 。

那么,如何基于 key 定位对应的桶呢?这是通过哈希函数(hash function)实现的。哈希函数的作用是将一个较大的输入空间映射到一个较小的输出空间。在哈希表中,输入空间是所有 key ,输出空间是所有桶(数组索引)。换句话说,输入一个 key ,我们可以通过哈希函数得到该 key 对应的键值对在数组中的存储位置

输入一个 key ,哈希函数的计算过程分为以下两步。

  1. 通过某种哈希算法 hash() 计算得到哈希值。
  2. 将哈希值对桶数量(数组长度)capacity 取模,从而获取该 key 对应的数组索引 index 。
index = hash(key) % capacity

随后,我们就可以利用 index 在哈希表中访问对应的桶,从而获取 value 。

设数组长度 capacity = 100、哈希算法 hash(key) = key ,易得哈希函数为 key % 100 。图 6-2 以 key 学号和 value 姓名为例,展示了哈希函数的工作原理。

#include <iostream>
#include <vector>
#include <string>
#include <stdexcept>
using namespace std;/*
6.1.2   哈希表简单实现
我们先考虑最简单的情况,仅用一个数组来实现哈希表。在哈希表中,我们将数组中的每个空位称为桶(bucket),每个桶可存储一个键值对。因此,查询操作就是找到 key 对应的桶,并在桶中获取 value 。那么,如何基于 key 定位对应的桶呢?这是通过哈希函数(hash function)实现的。哈希函数的作用是将一个较大的输入空间映射到一个较小的输出空间。在哈希表中,输入空间是所有 key ,输出空间是所有桶(数组索引)。换句话说,输入一个 key ,我们可以通过哈希函数得到该 key 对应的键值对在数组中的存储位置。输入一个 key ,哈希函数的计算过程分为以下两步。通过某种哈希算法 hash() 计算得到哈希值。
将哈希值对桶数量(数组长度)capacity 取模,从而获取该 key 对应的数组索引 index 。index = hash(key) % capacity
随后,我们就可以利用 index 在哈希表中访问对应的桶,从而获取 value 。设数组长度 capacity = 100、哈希算法 hash(key) = key ,易得哈希函数为 key % 100 。*/
/* 键值对 */
struct Pair {
public:int key;string val;Pair(int key, string val) {this->key = key;this->val = val;}
};/* 基于数组实现的哈希表 */
class ArrayHashMap {
private:vector<Pair*> buckets;public:// 构造函数,初始化100个桶为空指针ArrayHashMap() {buckets = vector<Pair*>(100, nullptr);}// 析构函数,释放所有动态分配的Pair对象~ArrayHashMap() {for (auto& pair : buckets) {delete pair;}buckets.clear();}// 哈希函数:取key模100(桶数)int hashFunc(int key) {return key % buckets.size();}// 查询操作,返回对应值或空字符串string get(int key) {int index = hashFunc(key);Pair* pair = buckets[index];if (pair == nullptr || pair->key != key) {// 可能发生哈希冲突,需匹配keyreturn "";}return pair->val;}// 添加或更新操作void put(int key, const string& val) {int index = hashFunc(key);// 如果桶为空,直接插入if (buckets[index] == nullptr) {buckets[index] = new Pair(key, val);}else {// 如果桶已有元素,判断key是否相同if (buckets[index]->key == key) {// 更新值buckets[index]->val = val;}else {// 发生哈希碰撞, 简单冲突策略:覆盖(替换现有元素)delete buckets[index]; // 删除旧的buckets[index] = new Pair(key, val);}}}// 删除操作void remove(int key) {int index = hashFunc(key);if (buckets[index] != nullptr && buckets[index]->key == key) {delete buckets[index];buckets[index] = nullptr;}}// 获取所有的Key-Value对(只返回非空槽)vector<Pair*> pairSet() {vector<Pair*> res;for (auto& pair : buckets) {if (pair != nullptr) {res.push_back(pair);}}return res;}// 获取所有的Keyvector<int> keySet() {vector<int> keys;for (auto& pair : buckets) {if (pair != nullptr) {keys.push_back(pair->key);}}return keys;}// 获取所有的值vector<string> valueSet() {vector<string> values;for (auto& pair : buckets) {if (pair != nullptr) {values.push_back(pair->val);}}return values;}// 打印哈希表内容void print() {for (auto& kv : pairSet()) {cout << kv->key << " -> " << kv->val << endl;}}
};/* 简单测试例子 */
int main() {ArrayHashMap map;// 添加map.put(1, "Apple");map.put(2, "Banana");map.put(102, "Orange"); // 102和2可能冲突// 打印map.print();// 获取cout << "Key 1: " << map.get(1) << endl;cout << "Key 2: " << map.get(2) << endl;cout << "Key 102: " << map.get(102) << endl;// 更新map.put(2, "Grape");cout << "After update:" << endl;map.print();// 删除map.remove(1);cout << "After deleting key 1:" << endl;map.print();system("pause");  return 0;
}

6.1.3   哈希冲突与扩容

从本质上看,哈希函数的作用是将所有 key 构成的输入空间映射到数组所有索引构成的输出空间,而输入空间往往远大于输出空间。因此,理论上一定存在“多个输入对应相同输出”的情况

对于上述示例中的哈希函数,当输入的 key 后两位相同时,哈希函数的输出结果也相同。例如,查询学号为 12836 和 20336 的两个学生时,我们得到:

12836 % 100 = 36
20336 % 100 = 36

如图 6-3 所示,两个学号指向了同一个姓名,这显然是不对的。我们将这种多个输入对应同一输出的情况称为哈希冲突(hash collision)

http://www.lryc.cn/news/605518.html

相关文章:

  • 反射之专题
  • 将本地项目关联并推送到已有的 GitHub 仓库
  • 第13届蓝桥杯C++青少组中/高级组选拔赛2022年1月22日真题
  • 可计算存储(Computational Storage)与DPU(Data Processing Unit)的技术特点对比及实际应用场景分析
  • #C语言——学习攻略:深挖指针路线(五)--回调函数,qsort函数,qsort函数的模拟实现
  • axios封装对比
  • 《C#与.NET Core跨平台开发的融合架构与实践逻辑》
  • 编程语言Java——核心技术篇(六)解剖反射:性能的代价还是灵活性的福音?
  • 【[CSP-J 2022] 上升点列】
  • RabbitMQ 的死信队列完整指南 (With Spring Boot)
  • 从遮挡难题到精准测量:激光频率梳技术如何实现深孔 3D 轮廓的 2um 级重复精度?
  • Mac上优雅简单地使用Git:从入门到高效工作流
  • 05百融云策略引擎项目交付-laravel实战完整交付定义常量分文件配置-独立建立lib类处理-成功导出pdf-优雅草卓伊凡
  • LCM中间件入门(1):工作原理核心概念及Ubuntu环境下的C++实践
  • 【Debian】4-‌2 Gitea搭建
  • Git踩坑
  • windows服务器 maven 配置环境变量,验证maven环境变量是否配置成功
  • es的histogram直方图聚合和terms分组聚合
  • Ubuntu/Debian 搭建 Nginx RTMP 服务器全攻略
  • [Broken IOS] 配置CLI | 终端用户界面TUI
  • 分布式ID方案(标记)
  • 【Linux】linux基础开发工具(二) 编译器gcc/g++、动静态库感性认识、自动化构建-make/Makefile
  • BasicAuthenticationFilter处理 HTTP 基本认证(Basic Authentication)的核心过滤器详解
  • 打破数据质量瓶颈:用n8n实现30秒专业数据质量报告自动化
  • 50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | LiveUserFilter(实时用户过滤组件)
  • ensp安全策略实验
  • 【工具】NVM完全指南:Node.js版本管理工具的安装与使用详解
  • 嵌入式仿真教学的革新力量:深圳航天科技创新研究院引领高效学习新时代
  • 【n8n】如何跟着AI学习n8n【03】:HTTPRequest节点、Webhook节点、SMTP节点、mysql节点
  • 从“碎片化”到“完美重组”:IP报文的分片艺术