哈希查找算法

哈希查找算法哈希查找算法哈希查找算法又称散列查找算法 是一种借助哈希表 散列表 查找目标元素的方法 查找效率最高时对应的时间复杂度为 O 1 哈希查找算法适用于大多数场景 既支持在有序序列中查找目标元素 也支持在无序序列中查找目标元素 讲解哈希查找算法之前 我们首先要搞清楚什么是哈希表 哈希表是什么哈希表 Hashtable 又称散列表 是一种存储结构 通常用来存储多个元素 和其它存储结构 线性表 树等 相比 哈希表查找目标元素的效率非常高 每个存储到哈希表中的元素 都配有一个唯一的标识 又称 索引 或者

哈希查找算法

哈希查找算法又称散列查找算法,是一种借助哈希表(散列表)查找目标元素的方法,查找效率最高时对应的时间复杂度为 O(1)。

哈希查找算法适用于大多数场景,既支持在有序序列中查找目标元素,也支持在无序序列中查找目标元素。讲解哈希查找算法之前,我们首先要搞清楚什么是哈希表。

哈希表是什么

哈希表(Hash table)又称散列表,是一种存储结构,通常用来存储多个元素。

和其它存储结构(线性表、树等)相比,哈希表查找目标元素的效率非常高。每个存储到哈希表中的元素,都配有一个唯一的标识(又称“索引”或者“键”),用户想查找哪个元素,凭借该元素对应的标识就可以直接找到它,无需遍历整个哈希表。

多数场景中,哈希表是在数组的基础上构建的,下图给大家展示了一个普通的数组:

在这里插入图片描述

图 1 数组

使用数组构建哈希表,最大的好处在于:可以直接将数组下标当作已存储元素的索引,不再需要为每个元素手动配置索引,极大得简化了构建哈希表的难度。

我们知道,在数组中查找一个元素,除非提前知晓它存储位置处的下标,否则只能遍历整个数组。哈希表的解决方案是:各个元素并不从数组的起始位置依次存储,它们的存储位置由专门设计的函数计算得出,我们通常将这样的函数称为哈希函数。

哈希函数类似于数学中的一次函数,我们给它传递一个元素,它反馈给我们一个结果值,这个值就是该元素对应的索引,也就是存储到哈希表中的位置。

举个例子,将 {20, 30, 50, 70, 80} 存储到哈希表中,我们设计的哈希函数为 y=x/10,最终各个元素的存储位置如下图所示:

在这里插入图片描述

图 2 哈希表存储结构

在图 2 的基础上,假设我们想查找元素 50,只需将它带入 y=x/10 这个哈希函数中,计算出它对应的索引值为 5,直接可以在数组中找到它。借助哈希函数,我们提高了数组中数据的查找效率,这就是哈希表存储结构。

构建哈希表时,哈希函数的设计至关重要。假设将 {5, 20, 30, 50, 55} 存储到哈希表中,哈希函数是 y=x%10,各个元素在数组中的存储位置如下图所示:

在这里插入图片描述

图 3 哈希表发生哈希冲突

可以看到,5 和 55 以及 20、30 和 50 对应的索引值是相同的,它们的存储位置发生了冲突,我们习惯称为哈希冲突或者哈希碰撞。设计一个好的哈希函数,可以降低哈希冲突的出现次数。哈希表提供了很多解决哈希冲突的方案,比如线性探测法、再哈希法、链地址法等。

本节我们使用线性探测法解决哈希冲突,解决方法是:当元素的索引值(存储位置)发生冲突时,从当前位置向后查找,直至找到一个空闲位置,作为冲突元素的存储位置。仍以图 3 中的哈希表为例,使用线性探测法解决哈希冲突的过程是:

元素 5 最先存储到数组中下标为 5 的位置;

元素 20 最先存储到数组中下标为 0 的位置;

元素 30 的存储位置为 0,和 20 冲突,根据线性探测法,从下标为 0 的位置向后查找,下标为 1 的存储位置空闲,用来存储 30;

元素 50 的存储位置为 0,和 20 冲突,根据线性探测法,从下标为 0 的位置向后查找,下标为 2 的存储位置空闲,用来存储 50;

元素 55 的存储位置为 5,和 5 冲突,根据线性探测法,从下标为 5 的位置向后查找,下标为 6 的存储位置空闲,用来存储 55。

借助线性探测法,最终 {5, 20, 30, 50, 55} 存储到哈希表中的状态为:

在这里插入图片描述

图 4 线性探测法解决哈希冲突

对于发生哈希冲突的哈希表,尽管查找效率会下降,但仍比一些普通存储结构(比如数组)的查找效率高。

哈希查找算法

哈希查找算法就是利用哈希表查找目标元素的算法。对于给定的序列,该算法会先将整个序列存储到哈希表中,然后再查找目标元素。

例如,哈希查找算法查找 {5, 20, 30, 50, 55} 序列中是否有 50 这个元素,实现的伪代码如下:

结合伪代码,如下是使用哈希查找算法在 {5, 20, 30, 50, 55} 序列中查找 50 的 C 语言程序:

#include 
  
    #define N 10 //指定哈希表的长度 //自定义哈希函数 int hash(int value) { return value % 10; } //创建哈希表 void creatHash(int arr[5], int hashArr[N]) { int i,index; //将序列中每个元素存储到哈希表 for (i = 0; i < 5; i++) { index = hash(arr[i]); while(hashArr[index % N] != 0) { index++; } hashArr[index] = arr[i]; } } //实现哈希查找算法,hashArr 表示哈希表,value 为要查找的目标元素 int hash_search(int* hashArr, int value) { int hashAdd = hash(value); //查找目标元素所在的索引 while (hashArr[hashAdd] != value) { // 如果索引位置不是目标元素,则发生了碰撞 hashAdd = (hashAdd + 1) % N; // 根据线性探测法,从索引位置依次向后探测 //如果探测位置为空,或者重新回到了探测开始的位置(即探测了一圈),则查找失败 if (hashArr[hashAdd] == 0 || hashAdd == hash(value)) { return -1; } } //返回目标元素所在的数组下标 return hashAdd; } int main() { int hashAdd; int hashArr[N] = { 0 }; int arr[5] = { }; creatHash(arr, hashArr); hashAdd = hash_search(hashArr, 50); //如果返回值为 -1,表明查找失败,反之则返回目标元素所在的位置 if (hashAdd == -1) { printf("查找失败\n"); } else { printf("查找成功,目标元素所在哈希表中的下标为:%d", hashAdd); } return 0; } 
  

如下是使用哈希查找算法在 {5, 20, 30, 50, 55} 序列中查找 50 的 Java 程序:

public class Demo { //哈希函数 public static int hash(int value) { return value % 10; } //创建哈希表 public static void creatHash(int [] arr,int [] hashArr) { int i,index; //将序列中每个元素存储到哈希表 for (i = 0; i < 5; i++) { index = hash(arr[i]); while(hashArr[index % 10] != 0) { index++; } hashArr[index] = arr[i]; } } //实现哈希查找算法 public static int hash_serach(int [] hashArr,int value) { //查找目标元素对应的索引值 int hashAdd = hash(value); while (hashArr[hashAdd] != value) { // 如果索引位置不是目标元素,则发生了碰撞 hashAdd = (hashAdd + 1) % 10; // 根据线性探测法,从索引位置依次向后探测 //如果探测位置为空,或者重新回到了探测开始的位置(即探测了一圈),则查找失败 if (hashArr[hashAdd] == 0 || hashAdd == hash(value)) { return -1; } } //返回目标元素所在的数组下标 return hashAdd; } public static void main(String[] args) { int [] arr = new int[] {5, 20, 30, 50, 55}; int[] hashArr = new int[10]; //创建哈希表 creatHash(arr,hashArr); // 查找目标元素 50 位于哈希表中的位置 int hashAdd = hash_serach(hashArr,50); if(hashAdd == -1) { System.out.print("查找失败"); }else { System.out.print("查找成功,目标元素所在哈希表中的下标为:" + hashAdd); } } } 

如下是使用哈希查找算法在 {5, 20, 30, 50, 55} 序列中查找 50 的 Python 程序:

# 自定义哈希函数 def hash(hashArr,value): return value % len(hashArr) #创建哈希表 def createHash(arr,hashArr): for ele in arr: index = hash(hashArr,ele) while hashArr[index % len(hashArr)] != 0: index = index + 1 hashArr[index] = ele # 实现哈希查找算法,hashArr 表示哈希表,value 为要查找的目标元素 def hash_search(hashArr,value): hashAdd = hash(hashArr,value) # 查找目标元素所在的索引 while hashArr[hashAdd] != value: # 如果索引位置不是目标元素,则发生了碰撞 hashAdd = (hashAdd + 1) % len(hashArr) # 根据线性探测法,从索引位置依次向后探测 #如果探测位置为空,或者重新回到了探测开始的位置(即探测了一圈),则查找失败 if (hashArr[hashAdd] == 0) or (hashAdd == hash(hashArr,value)): return -1 return hashAdd # 返回目标元素所在的数组下标 #待查找序列 arr = [5, 20, 30, 50, 55] #构建哈希表 hashArr = [0]*10 createHash(arr,hashArr) # 查找元素 50 的位置 hashAdd = hash_search(hashArr,50) if hashAdd == -1: print("查找失败\n") else: print("查找成功,目标元素所在哈希表中的下标为 %d" % (hashAdd)) 

以上程序的输出结果均为:

查找成功,目标元素所在哈希表中的下标为 2

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/211610.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午10:02
下一篇 2026年3月18日 下午10:02


相关推荐

  • linux 下vim的使用(学习必看!!重要)

    linux 下vim的使用(学习必看!!重要)vi与vimvi编辑器是所有Unix及Linux系统下标准的编辑器,他就相当于windows系统中的记事本一样,它的强大不逊色于任何最新的文本编辑器。他是我们使用Linux系统不能缺少的工具。由于对Unix及Linux系统的任何版本,vi编辑器是完全相同的,学会它后,您将在Linux的世界里畅行无阻。vim具有程序编辑的能力,可以以字体颜色辨别语法的正确性,方便程序设计;因为程序简单

    2025年6月10日
    8
  • layUI展示树状treetable树形表格完整代码

    layUI展示树状treetable树形表格完整代码前言:因项目功能需要,在shiro权限管理模块中需要使用树状展示,前端使用的layUI框架,在官网的开发文档上没有找到树状表格的内容。只有树状菜单的文档:https://www.layui.com/doc/modules/tree.html树状表格步骤如下:1、首先下载所需调用的文件。下载链接:https://download.csdn.net/download/qq_35393472/10…

    2022年6月14日
    85
  • ETH被冻结_微信冻结显示什么界面

    ETH被冻结_微信冻结显示什么界面如果我们的电脑在启动挖矿软件的时候,发现界面有停顿,Miner都会冻结。有时矿工会随机冻结,直到按下任何键。而我们要做下如下设置则可以解决这个问题。1、cmd命名行界面;2、右击窗口,点属性大力矿工致力于挖矿软件研究,欢迎大家加入群:621159725,一起讨论ETH挖矿。…

    2022年10月15日
    3
  • RegisterHotKey函数

    RegisterHotKey函数转载 RegisterHotK 实现 Alt E 的快捷键组合功能 2007 07 3009 48 问题提出 nbsp nbsp nbsp nbsp 有的程序需要自定义组合键完成一定功能 如何实现 nbsp nbsp 解决方法 nbsp nbsp nbsp nbsp RegisterHotK 函数原型及说明 nbsp nbsp nbsp nbsp BOOLRegister nbsp nbsp nbsp nbsp H

    2026年3月17日
    2
  • 详细讲解 移植Uboot到ARM9开发系统上

    详细讲解 移植Uboot到ARM9开发系统上首先了解ARMer9开发系统硬件设计上和三星原装SMDK2410之间的区别。让uboot在ARMer9开发系统上跑起来,目前只需要关注如下的硬件区别,解决了下面这个问题,uboot就可以在ARMer9开发系统上正常地从串口输出,进入提示符。很多命令都可以使用,当然有些命令需要做修改。 SMDK2410:norFlash是AMD的1M的;ARMer9:是IntelE28F

    2022年5月1日
    49
  • STM32之NVIC的深入详解

    STM32之NVIC的深入详解STM32NVIC中断嵌套

    2022年5月28日
    50

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号