哈希表概念

哈希表(散列表),是基于关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数(哈希函数),存放记录的数组叫做散列表。
上面所提到的哈希函数是指:有一个对应关系 f ,使得每个关键字和结构中一个唯一的存储位置相对应,这样在查找时,我们不需要像传统的查找算法那样进行比较,而是根据这个对应关系 f 找到给定值K的像f(K)。

哈希函数与哈希表

  • 这个hash函数并没有什么统一标准,它的核心思想就是就是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。
  • 这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数,这个消息可能是字符、数组、字符串等等。
  • 再使用哈希表进行查询的时候,就是再次使用哈希函数将key转换为对应的数组下标,并定位到该空间获取value,如此一来,就可以充分利用到数组的定位性能进行数据定位。
  • 拥有这样的hash存储结构的数据结构称为散列表,或者叫哈希表。
    哈希表一般基于数组实现,特定情况下结合链表,但是数组是哈希表基础数据结构。

为什么要有哈希表呢?
数组的特点是:寻址容易,插入和删除困难;而链表的特点是:寻址困难,插入和删除容易。那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表,哈希表有多种不同的实现方法,我接下来解释的是最常用的一种方法。

哈希算法

构造哈希函数的方法有很多。首先需要明确什么是“好” 的哈希算法。若对于关键字集合中的任一个关键字,经哈希函数映像到地址集合中任何一个地址的概率是相等的,则称此类哈希函数是均匀的(Uniform)哈希函数。换句话说,就是使关键字经过哈希函数得到一个“随机的地址”,以便使一组关键字的哈希地址均匀分布在整个地址区间中,从而减少冲突。

一个hash函数需要满足

  • 接受一个单一的参数,这个参数可以是任何类型,但是只能是一个。
  • 返回一个整型值(一般情况下)。
  • 输出的哈希地址均匀分布在整个地址区间中。
  • 对于两个相同的输入,输出必须相同。
  • 对于两个不同的输入,输出相同的概率需要做到非常小。
  • hash的计算不能过于复杂,时间复杂度尽可能地低。
    既然自己想不到比较好的hash算法,我们就来看看别人是怎么做的吧,下面是一些常用的hash算法:
    直接定址法
    取key的线性函数值作为hash值,value = a * key + b,a,b为常数。这一类散列码的特点是:对输入为整型数据而言,不会产生下标冲突。不产生冲突当然是最完美的状态,但是这种方式要求输入的key遵循一定的线性规律。
    例如:有一个01到07的部门人数统计表,其中,部门编号作为关键字,哈希函数取关键字自身。如下表所示:
地址
01
02
03
04
05
06
07
部门 人数
01 23
02 43
03 24
04 65
05 64
06 33
07 46

这样若要询问01部门有多少人,则只要查表的第01项即可。由于直接定址所得的地址集合和关键字集合的大小相同。因此,对于不同的关键字不会发生冲突,但是实际中能使用这种哈希函数的情况很少。
除留余数法
除留余数法:假设数组的长度为l,value = key % l,这一种散列码实现简单,运用比较多,但是如果输入的元素集合不具有一定的规律,比较容易产生冲突。数组的长度最好是质数,被除数为质数在一定程度上可以缓解数据堆积的问题。
除留余数法此方法为最常用的构造散列函数方法。对于散列表长为m的散列函数公式为:

f( key ) = key mod p ( p ≤ m )

mod是取模(求余数)的意思。事实上,这方法不仅可以对关键字直接取模,也可在折叠、平方取中后再取模。
显然在这里选取p的值至为关键,那么选取p值多少为合适呢?下面我们来举个例子看看:有一个关键字,它有7个记录。如果采用除留余数法,那么可以先尝试将散列函数设计为f(key) = key mod 7的方法。比如12 mod 7= 5,所以它存储在下标为5的位置。

地址 关键字
00 7
01 22
02 9
03 17
04 11
05 12
06 48
07 46

不过这也是存在冲突的可能的,像7、14、21、28、35……这些获得的下标都为零。
如何合理选取p值?
使用除留余数法的一个经验是,若散列表表长为m,通常p为小于或等于表长(最好接近m)的最小质数或不包含小于20质因子的合数。
举个例子: 某散列表的长度为100,散列函数H(k)=k%P,则P通常情况下最好选择哪个作为p呢?答案是97。
数字分析法
数字分析法即对关键字进行分析,取关键字的若干位进行或者组合进行hash计算,这一类散列码的特点是比较灵活,通常是结合其他hash函数来计算,可根据实际情况来做出调整,具有相当的灵活性。
有学生的生日数据如下:

学生序号 学生生日
0 1975.10.03
1 1975.10.03
2 1975.11.23
3 1975.03.02
4 1975.07.12
5 1975.04.21
6 1975.02.15

经分析,第一位,第二位,第三位、第四位重复的可能性大,取这四位造成冲突的机会增加,所以尽量不取前四位,取后四位比较好。
随机数法
加粗样式选择一个随机函数,取关键字的随机函数值为它的哈希地址,即H(key)= random(key),其中random为随机函数。通常,当关键字长度不等时采用此法构造哈希函数较恰当。
平方取中法
取关键字平方后中间几位作哈希地址。适于关键字长度不统一的情况,而且对于元素连续的输入,可以很好的将其散列均匀,而且相对于除法而言,乘法的执行速度更快,这个由硬件决定。
折叠法
将关键字分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和(舍去进位)作为哈希地址,这方法称为折叠法。
例如:每一种西文图书都有一个国际标准图书编号,它是一个10位的十进制数字,若要以它作关键字建立一个哈希表,当馆藏书种类不到10,000时,可采用此法构造一个四位数的哈希函数。
处理哈希冲突的方法

基本原则: 从hash函数的要求可以看到,事实上我们只能定义对于两个不同的输入,输出相同的概率尽可能小。
开放地址法
开放地址法有一个公式:Hi=(H(key)+di) MOD m i=1,2,…,k(k<=m-1)其中,m为哈希表的表长。di是产生冲突的时候的增量序列。
(1)-di值可能为1,2,3,…m-1,称线性探测再散列。如果di取1,则每次冲突之后,向后移动1个位置.
(2)-di取值可能为1,-1,4,-4,9,-9,16,-16,…kk,-kk(k<=m/2)称二次探测再散列。
(3)-di取值可能为伪随机数列。称伪随机探测再散列。
例如,在长度为7的哈希表中已填有关键字分别为9、16的记录(哈希函数 H(key) = key MOD 7),现有第2个记录,其关键字为16,由哈希函数得到哈希地址为2,产生冲突。若用线性探测再散列方法处理,得到下一个地址为3,仍冲突,继续算4,不冲突,填入哈希表。若用二次探测再散列,则应填入需要为1的位置。
线性探测再散列

地址 关键字
0
1
2 9
3 17
4 16
5
6

二次探测再散列

地址 关键字
0
1 16
2 9
3 17
4
5
6

多哈希法
设计多种哈希函数,可以避免冲突,但是冲突几率还是有的,函数设计的越好或越多都可以将几率降到最低。
拉链法
拉出一个动态链表代替静态顺序存储结构,可以避免哈希函数的冲突,不过缺点就是链表的设计过于麻烦,增加了编程复杂度。此法可以完全避免哈希函数的冲突。

左边很明显是个数组,数组的每个成员包括一个指针,指向一个链表的头,当然这个链表可能为空,也可能元素很多。我们根据元素的一些特征把元素分配到不同的链表中去,也是根据这些特征,找到正确的链表,再从链表中找出这个元素。

建立一个公共溢出区

这也是处理冲突的一种方法、假设哈希函数的值域为[ 0, m - 1 ],则设向量HashTable[ 0…m - 1 ]为基本表,每个分量存放一个记录,另设向量OverTable[0…v]为溢出表。所有关键字和基本表中关键字为同义词的记录,不管它们由哈希函数得到的哈希地址是什么,一旦发生冲突,都填入溢出表。
总结

优点:

不论哈希表中有多少数据,查找、插入、删除(有时包括删除)只需要接近常量的时间即0(1)的时间级。实际上,这只需要几条机器指令。
哈希表运算得非常快,在计算机程序中,如果需要在一秒种内查找上千条记录通常使用哈希表(例如拼写检查器)哈希表的速度明显比树快,树的操作通常需要O(N)的时间级。哈希表不仅速度快,编程实现也相对容易。
如果不需要有序遍历数据,并且可以提前预测数据量的大小。那么哈希表在速度和易用性方面是无与伦比的。

缺点:

它是基于数组的,数组创建后难于扩展,某些哈希表被基本填满时,性能下降得非常严重,所以程序员必须要清楚表中将要存储多少数据(或者准备好定期地把数据转移到更大的哈希表中,这是个费时的过程)。

大白话之哈希表和哈希算法相关推荐

  1. ds哈希查找—二次探测再散列_大白话之哈希表和哈希算法

    哈希表概念 哈希表(散列表),是基于关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数(哈希函数 ...

  2. python 哈希表_哈希表哪家强?编程语言找你来帮忙!

    点击关注上方"五分钟学算法", 设为"置顶或星标",第一时间送达干货. 转自编程技术宇宙 哈希表华山论剑 比特宇宙编程语言联合委员会准备举办一次大会,主题为哈希 ...

  3. 数据结构源码笔记(C语言):哈希表的相关运算算法

    //实现哈希表的相关运算算法 #include<stdio.h> #include<malloc.h> #include<string.h>#define MaxS ...

  4. PAT甲级1145 Hashing - Average Search Time:[C++题解]哈希表、哈希表开放寻址法、二次探测法、求平均查找次数

    文章目录 题目分析 题目链接 题目分析 来源:acwing 本题的分析见另一道PAT的题目:PAT甲级1078 Hashing:[C++题解]哈希表.哈希表开放寻址法.二次探测法链接的题目就是让建立h ...

  5. 哈希表及哈希冲突解决办法

    哈希表及哈希冲突解决办法 目录 什么是哈希表? 哈希表的数据结构 哈希冲突 哈希冲突解决办法 1. 什么是哈希表? 哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直 ...

  6. 除留余数法构造哈希表_哈希表算法原理

    基本概念 哈希表(Hash Table)是一种根据关键字直接访问内存存储位置的数据结构.通过哈希表,数据元素的存放位置和数据元素的关键字之间建立起某种对应关系,建立这种对应关系的函数称为哈希函数. 哈 ...

  7. 哈希表及哈希表查找相关概念(转)

    1. 哈希表的概念 对于动态查找表而言,1) 表长不确定:2)在设计查找表时,只知道关键字所属范围,而不知道确切的关键字.因此,一般情况需建立一个函数关系,以f(key)作为关键字为key的录在表中的 ...

  8. 哈希表添加哈希表(Hash Table,也叫散列表),是根据键(Key)而直接访问在内存存储位置的数据结构。typedef enum{ HASH_OK, -icoding

    哈希表添加 哈希表(Hash Table,也叫散列表),是根据键(Key)而直接访问在内存存储位置的数据结构.也就是说,它通过计算一个关于键值的函数,将所需查询的数据映射到表中一个位置来访问记录,这加 ...

  9. 【数据结构】哈希表、哈希值计算分析

    哈希表.哈希值计算分析 哈希表完整代码 引出哈希表 哈希表(Hash Table) 哈希冲突(Hash Collision) JDK1.8的哈希冲突解决方案 哈希函数 如何生成 key 的哈希值 In ...

最新文章

  1. 常用的linux命令与示例,linux常用命令及用法示例
  2. vb6 datagrid表格垂直居中_Word文档中表格的定位方式
  3. SAP HANA要改变什么?
  4. mysql保存中文异常Incorrect string value: '\xE4\xBD\xA0\xE5\xA5\xBD' for column'
  5. 文件不混淆_Android Studio配置反混淆
  6. Android复习02(ListView具体操作[很详细]、简单音乐播放器)
  7. 批量插入使用SqlBulkCopy
  8. python二维数组去重_PHP二维数组去重
  9. python hook_python_理解篇_钩子方法的理解
  10. mybatis SqlMapConfig.xml
  11. 微博转发的内容如何实现点击人名跳转到个人主页
  12. 网络雇佣军 Void Balaur,有组织有纪律,且从不休长假
  13. 小甲鱼python课后题简书_MOOC_Python语言程序设计(嵩天)课后练习_第二周
  14. 如何在Mac上将您的Apple ID更改为其他电子邮件地址?
  15. 绕过某省某大学校园网的探索(处女作)
  16. excel如何把顺序倒过来_在excel中怎么使文字颠倒顺序反过来显示呢?
  17. python中平方和_python的平方和怎么理解?
  18. 小程序地理位置接口申请
  19. HTB_Responder 综合靶机 菜菜被虐现场实录
  20. 华为擎云G540笔记本怎么U盘重装电脑系统详细教学

热门文章

  1. 《深入理解计算机系统》课本第七章自学笔记——20135203齐岳
  2. Unity遮罩 反向遮罩实现
  3. 安卓手机远程控制DIY智能家居设备(ESP8266)
  4. 线性结构-前缀和和差分
  5. linux点唱机安装教程,咪哒Minik移动ktv点唱机安装教程(文字版)
  6. python面向对象 : 反射和内置方法
  7. ubuntu 20.04 | 常用软件 必要配置
  8. leetcode学习打卡--572. 另一个树的子树(递归,二叉树遍历)
  9. 从虚拟走向现实!数字孪生迎来崛起
  10. 如何使用 Backblaze 和 Cloudflare 搭建免费 CDN - 让白-piao进行到底