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

C++简单实现哈希查找

C++ 简单实现哈希查找

1. 哈希冲突

哈希表中可能会出现哈希冲突,即多个数据项映射到相同的桶。

常见的冲突解决方法包括链地址法(Chaining)和线性探测法(Linear Probing)。

  • 使用链地址法时,每个桶维护一个链表,哈希冲突的数据项被插入到链表中。查找时,遍历链表以查找目标数据项。
  • 使用线性探测法时,如果一个桶已经被占用,算法会线性地探测下一个可用的桶,直到找到目标数据项或空桶。这种方法需要特别处理删除操作。

2. 算法执行过程

  • 初始化哈希表:创建一个固定大小的数组,将每个元素的值设置为其索引。
  • 插入元素:根据要插入的元素计算其哈希值,然后将该元素插入到对应索引的位置。如果发生哈希冲突(即两个不同的元素具有相同的哈希值),则在相应索引位置存储一个链表,链表中存储具有相同哈希值的所有元素。
  • 查找元素:首先计算要查找元素的哈希值,然后根据哈希值找到对应的索引位置。如果该位置没有其他元素,说明找到了目标元素;否则,在对应索引位置的链表中继续查找。
  • 删除元素:如果要删除的元素恰好等于目标元素,那么直接删除即可;否则,需要找到该元素所在的链表,并从链表中删除该元素。
  • 扩容:当哈希表的大小超过了数组的最大容量时,需要对数组进行扩容,即创建一个新的更大的数组,并将原数组中的元素重新插入到新数组中。同时,还需要重新计算所有元素的哈希值,并将它们插入到新数组的相应位置。****

3. 例题

例题:

有序表 A[20] = {4,6,12,20,28,38,50,70,88,100},使用哈希查找算法找出元素 20

4. 步骤

为了使用哈希查找方法实现有序表A[20]:

  • 我们需要首先定义哈希表的大小。哈希表的大小通常比表中元素的最大值要大,以确保所有元素都能被存储,并且有足够的空间来解决哈希冲突。
  • 接下来,我们需要定义一个哈希函数。一个简单的哈希函数可以是将元素值除以哈希表大小,然后取整数部分。
  • 在这个例子中,我们的哈希表大小为10,所以哈希函数可以是 h(x) = x % 10。对于整数,这个函数将把每个元素映射到一个在0到9之间的位置。
  • 现在,我们可以开始将元素插入哈希表中。由于我们使用线性探索来解决冲突,当冲突发生时,我们将在哈希表中的当前位置开始线性搜索,直到找到一个空位置为止。

以下是插入过程的步骤:

  1. 初始化哈希表:hashTable[10] = {NULL},其中 NULL 表示空位置。
  2. 对于表A中的每个元素x:
    • 计算哈希值:h(x) = x % 10
    • 确定哈希位置:index = h(x)
    • 如果 hashTable[index]NULL,则将x插入到 hashTable[pos]
    • 如果 hashTable[index]不为 NULL,则进行线性探索,寻找空位置。线性探索可以从 index + 1 开始,直到找到空位置。
  3. 重复步骤2,直到表A中的所有元素都被插入到哈希表中。
    下面是具体的插入过程:
  • 对于4,h(4) = 4 % 10 = 4, hashTable[4] 为空,插入。
  • 对于20,h(20) = 12 % 10 = 0hashTable[0] 为空,插入。
  • 对于50,h(50) = 50 % 10 = 0,但 hashTable[0] 已被占用,进行线性探索,hashTable[1] 为空,插入。
  • 依此类推,直到所有元素都被插入到哈希表中。

最终的哈希表 hashTable 将包含有序表 A 的元素,可能有一些位置为 NULL,表示在插入过程中没有发生冲突。

为了查找表中的一个元素x,我们同样使用哈希函数计算其哈希值,然后在哈希表中定位该值。

如果在定位的位置找到了元素 x,则查找成功;如果找到了 NULL,则说明元素 x 不在表中。

需要注意的是,线性探索虽然简单,但在高负载情况下(即哈希表的装载因子较高)会导致性能下降,因为可能会出现较长的查找链。

在实际应用中,可能会采用更复杂的哈希函数和冲突解决策略来提高效率。

5. 代码

#include <iostream>
#include <vector>using namespace std;// 哈希表的大小
const int HASH_TABLE_SIZE = 10;
// 哈希表
vector<int> hashTable(HASH_TABLE_SIZE, 0);  // 初始化为NULL
// 哈希函数
int hashFunction(int key) {return key % HASH_TABLE_SIZE;
}
// 线性探查
int linearProbing(int key) {int index = hashFunction(key);while (hashTable[index] != 0 && hashTable[index] != key) {index = (index + 1) % HASH_TABLE_SIZE;  // 线性探查}return index;
}// 插入键值对
void insert(int key) {int index = linearProbing(key);if (hashTable[index] == 0) {hashTable[index] = key;} else {cout << "冲突发生,键 " << key << " 已存在。" << endl;}
}// 查找键
int search(int key) {int index = hashFunction(key);while (hashTable[index] != 0) {if (hashTable[index] == key) {return index;}index = (index + 1) % HASH_TABLE_SIZE;  // 线性探查}return -1;  // 未找到键
}int main() {// 初始化有序表int A[10] = {4, 6, 12, 20, 28, 38, 50, 70, 88, 100};// 插入元素for (int i = 0; i < 10; ++i) {insert(A[i]);}for (int i = 0; i < 10; ++i) {cout << hashTable[i] << " ";}cout << endl;// 搜索元素int keyToSearch = 20;int index = search(keyToSearch);if (index != -1) {cout << "键 " << keyToSearch << " 在哈希表中的位置是: " << index << endl;} else {cout << "键 " << keyToSearch << " 在哈希表中未找到。" << endl;}return 0;
}

6. 使用场景

  1. 字典和关联数组:哈希表常用于实现字典和关联数组,其中键映射到值。这样可以快速查找特定键对应的值。
  2. 缓存:哈希表用于数据缓存,允许快速访问已经检索的数据,以减少对慢速存储介质(如磁盘)的访问。
  3. 数据库索引:在数据库管理系统中,哈希表用于构建索引,以便快速检索和访问数据库中的数据。这在大型数据库中非常有用。
  4. 散列集合:编程语言中的集合(Set)和散列集合(HashSet)通常使用哈希表来实现,以便快速查找成员。
  5. 数据去重:哈希表可用于检测和删除重复数据项。通过将数据插入哈希表并检查是否已经存在,可以有效去重。
  6. 认证和授权:哈希表可用于存储用户凭据(如用户名和密码)或授权令牌,以便快速验证用户身份。
  7. 编译器符号表:编程语言编译器使用哈希表来管理符号表,以便快速查找变量、函数和其他符号的定义和引用。
  8. 数据分布:在分布式计算中,哈希表用于确定数据项应该存储在哪个节点或分片,以实现均匀的数据分布。
  9. 缓存管理:缓存管理系统通常使用哈希表来跟踪缓存的内容,以加速数据检索。
  10. 路由表:网络路由器使用哈希表来管理路由表,以确定数据包的转发路径。
  11. 文件系统索引:文件系统通常使用哈希表来维护文件索引,以加速文件查找和访问。
  12. 编码查找表:在编码和解码中,哈希表可用于存储查找表,以实现快速的字符或编码查找。

相关文章:

  • TypeScript再学习(1)数据类型
  • Docker之docker compose!!!!
  • 数据结构与算法2-俩变量值交换、理解异或位运算
  • 还敢自学黑客,骂醒一个算一个(网络安全/信息安全)
  • 【Android】【Bluetooth Stack】蓝牙音乐协议分析之音频控制与信息加载(超详细)
  • 二叉树的遍历及线索二叉树试题(三)
  • 【CMake】所见所闻所学
  • 【蓝桥杯-单片机】基于定时器的倒计时程序设计
  • 基础:TCP四次挥手做了什么,为什么要挥手?
  • 编程题:相同数字的积木游戏(Java)
  • 暴力快速入门强化学习
  • 2024年阿里云服务器地域和可用区所在地区城市分布表
  • MT2191 整数大小比较(高精度)
  • React—— props校验(非typescript校验类型)
  • 目标检测——PP-YOLO算法解读
  • angular2 简述
  • httpie使用详解
  • Java多态
  • Linux各目录及每个目录的详细介绍
  • NLPIR语义挖掘平台推动行业大数据应用服务
  • React 快速上手 - 07 前端路由 react-router
  • Transformer-XL: Unleashing the Potential of Attention Models
  • vue:响应原理
  • windows下如何用phpstorm同步测试服务器
  • 阿里研究院入选中国企业智库系统影响力榜
  • 爱情 北京女病人
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 分享几个不错的工具
  • 前端之Sass/Scss实战笔记
  • 如何进阶一名有竞争力的程序员?
  • 深度解析利用ES6进行Promise封装总结
  • 使用parted解决大于2T的磁盘分区
  • 微信小程序开发问题汇总
  • 写代码的正确姿势
  • 用Visual Studio开发以太坊智能合约
  • 自定义函数
  • No resource identifier found for attribute,RxJava之zip操作符
  • Python 之网络式编程
  • ​​快速排序(四)——挖坑法,前后指针法与非递归
  • ​Linux·i2c驱动架构​
  • ​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​
  • # Swust 12th acm 邀请赛# [ K ] 三角形判定 [题解]
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • #100天计划# 2013年9月29日
  • #ifdef 的技巧用法
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • #中的引用型是什么意识_Java中四种引用有什么区别以及应用场景
  • $forceUpdate()函数
  • (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (70min)字节暑假实习二面(已挂)
  • (8)Linux使用C语言读取proc/stat等cpu使用数据
  • (8)STL算法之替换
  • (el-Transfer)操作(不使用 ts):Element-plus 中 Select 组件动态设置 options 值需求的解决过程