当前位置: 首页 > 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.dtcms.com/a/308032.html

相关文章:

  • Docker 实战 -- cloudbeaver
  • C语言---结构体(格式、用法、嵌套、初始化)、共用体、枚举类型、typedef类型
  • 【RAG Query Expansion论文解析】用 LLM 进行查询扩展 (Query Expansion)
  • 在MySQL中DECIMAL 类型的小数位数(Scale)如何影响分组查询?
  • 30天入门Python(基础篇)——第25天:标准库学习之OS模块
  • 一次 web 请求响应中,通常那个部分最耗时?
  • git ETAS包 使其可以本地编辑
  • 借助于llm将pdf转化为md文本
  • PDF源码解析
  • 数据结构第4问:什么是栈?
  • CUDA系列之CUDA安装与使用
  • freeRTOS 消息队列
  • Cesium 快速入门(三)Viewer:三维场景的“外壳”
  • 【MySQL】MySQL大偏移量查询优化方案
  • 若依框架-前端二次开发快速入门简述
  • [硬件电路-109]:模拟电路 - 自激振荡器的原理,一种把直流能量转换成交流信号的装置!
  • Linux软件包管理器深度解析:从概念到实战
  • React开发依赖分析
  • TRAE 软件使用攻略
  • 快速搭建Node.js服务指南
  • python制作的软件工具安装包
  • c# net6.0+ 安装中文智能提示
  • 前端框架Vue3(二)——Vue3核心语法之OptionsAPI与CompositionAPI与setup
  • 超体积指标(Hypervolume Indicator,S 度量)详析
  • 【JMeter】性能测试脚本录制及完善
  • 辐射源定位方法简述
  • 【BUUCTF系列】[HCTF 2018]WarmUp1
  • 网络编程-IP
  • 计算机网络:什么是光猫
  • Hyperbrowser MCP:重新定义网页抓取与浏览器自动化的AI驱动工具