首页 > 其他分享 >哈希表(【通俗易懂】知识点讲解,可速通,小白友好)

哈希表(【通俗易懂】知识点讲解,可速通,小白友好)

时间:2024-12-04 20:28:37浏览次数:6  
标签:知识点 Hash 哈希 索引 可速通 关键字 key 数据

一、哈希表的目的

哈希表是用在查找问题中的。

我们知道,一条数据包含了关键字和其他信息,所以一般查找问题的流程是:根据某条数据的关键字(key),在一个数据结构中(可能是线性表,也可能其他存储数据的结构),查找这条数据全部的内容。

哈希表的目的是,只要知道了要查找数据的关键字,那么就可以立刻得到存储这条数据的地址(比如顺序表的索引就可以看做地址),然后根据这个地址去到相应位置上,就得到了一条数据的全部内容。

二、哈希表是一张怎样的表

首先,哈希表可以简单认为是一个数组,它每一位都有一个索引,每个位置上存有数据,这个数据是全部的数据,既包含某条数据的关键字,也包含这条数据的其他部分。

当你想查找关键字为k的数据时,首先通过哈希函数(也称为散列函数),得到关键字为k的数据在哈希表中的索引,即index=Hash(k),然后就可以根据索引,在哈希表中找到索引index处存储的内容。

三、哈希函数和这个函数的构造方法

描述地专业一点,哈希函数是从关键字集合到索引值集合的映射。

换言之,就是通过对一个关键字k进行一波操作,得到一个索引值,所谓的【哈希函数的构造方法】,说白了就是在设计哈希函数的时候去研究这波操作怎么搞。

这里列举7种基本方法。

1.直接定址法

用式子描述,就是:index=Hash(key) = a·key + b (a、b为常数)  这里的关键字肯定是数字。

2.数字分析法

将一个关键字(比如身份证号)取其中的几位,然后做一些处理得到一个值。

有三个问题需要多提一嘴。

第一,该方法一般适用于关键字位数很多的情况,反正肯定比索引值位数多,最经典的例子就是身份证号。

第二,从关键字中取几位时,取法有讲究。要满足:各种符号在该位上出现的频率大致相同。

比如身份证号,有些位上的数字出现的频率是大致相同的,比如生日位,这个不确定性很强,所以0-9出现的频率不相上下;But,有些位不是如此,比如开头表示省的两位。如果我的所有数据都是取自某个省,那么其实开头两位都是一样的,也就是说,其他数字基本不可能出现在前两位,这样的位就不满足【各种符号在该位上出现的频率大致相同】。

第三,取出几位之后,要做什么样的处理才能得到索引值呢?

这个不一定,视具体情况而定。比如身份证号,假设我取了5位,5个数字分别为2,4,5,6,7。那么我的处理可以是2*10^4+4*10^3+5*10^2+6*10^1+7*10^0,这样就可以得到24567这个数字 ,作为索引值。

不过24567这个索引值很大,如果我的全部数据的个数也是几万这个数量级,那勉强还可以,可如果我只有80条数据,那索引值比数据总数还大,就不是一个好的构造,可以少取几位,比如取两位,这样最后得到的索引值是两位数,与数据总数在数量级上差不多,还算不错。

3.平方取中法

对关键字平方后,按哈希表大小,取中间的若干位作为哈希地址。

举个栗子。

关键字假设是1234,1234的平方是1522756,那我可以取2作为索引,当然也可以取22,取227,取52275,这个取多少,取决于哈希表有多大,比如哈希表只有80位,索引值取了52275,这不合适。

4.折叠法

第一步,将关键字自左向右分成若干个位数相等的部分,最后一部分位数可以少一点。

比如15698746,分成156  987  46三部分。

第二步,把每部分看做数字,直接相加(这个方法叫移位法)

156+987+46=1189

第三步,把得到的数字1189,从右边取几位作为索引,比如取个9,或者是取89,具体取多少也是要参考哈希表的长度。

另外,第二步还可以换种方法,叫间界叠加法。

其实就是把每部分的数字倒过来,651+789+64=xxx这样子。

5.除留余数法

index=Hash(key)=key mod p (p是一个整数)

这是很简单也很常用的办法。

关于p的取法,一般取不大于哈希表长度的最小素数。

6.乘余取整法

关键字key乘以A,取小数部分,然后乘B,取整,作为索引值。

其中,A介于0和1之间。取小数部分可以通过模1来实现。

7.随机数法

index=Hash(key) = random ( key )

这里的random是伪随机函数。

这种方法主要用于关键字长度不一样的情况,比如有的数据的关键字很长,另一些数据的关键字却很短。

四、哈希冲突的处理办法

先唠唠什么是哈希冲突。

简单来讲,如果两个不同的关键字经过哈希函数后,得到了同一个索引值,那么意味着两条数据要存到同一个位置,这显然不可以,这就叫哈希冲突。

说得专业点,哈希函数是从关键字集合到索引值集合的映射,如果这个映射不是单射,就会有哈希冲突。

那么解决哈希冲突的办法有哪些呢?这里给出四种办法。

1.开放定址法

基本思路:

如果发生了冲突,比如在索引为i的地方放了数据,之后又要在这个位置放数据,这就冲突了。

简单粗暴的解决思路是,既然这个地方有数据占用了,那索性【第二个本来应该放在这的数据】,就不和【第一次放进来的数据】抢位置了,而是选择其他空位置放。

那么如何选择其他空位置呢?这里有三种寻找空位置的办法。

(1)线性探测法

公式如下:indexi=(Hash(key)+di)mod m,i=1,2,……,m-1

其中,m是哈希表长度,确保算出来的索引值始终位于0到m-1

di是增量序列:1,2,…,m-1(听不懂往下看)

如果Hash(key)对应的索引位置没有元素,就直接存入,否则就按照上面的式子,将Hash(key)+1然后模m,得到新的索引,如果这个索引位置没有存数据,就可以直接把当前数据存入,否则就计算Hash(key)+2,模m,判断这个索引位置是否为空,若空就存入不空就算Hash(key)+3再mod m,直到找到空位置。

以上就是线性探测法的过程。

查找的时候,根据一个关键字k,算出Hash(key),但是发现这时候存的数据的关键字不是k,那就说明,有可能在哈希表构建之初,关键字为k的数据在索引为Hash(k)的地方发生了哈希冲突,所以采用线性探测法移动到别的位置了,那么如果一开始的冲突处理机制已经确定好了,那么这时候就可以按照同样的机制去找关键字为k的数据。

(2)二次探测法

indexi=(Hash(key)+di) mod m

di为增量序列,1^2,-1^2,2^2,-2^2,3^2,-3^2……,(m/2)^2,-(m/2)^2

这个过程与线性探测法基本一致,只不过发生哈希冲突后,找新的索引时,d按照序列的顺序依次取值,直到算出来的index对应的位置是空位置为止,然后把数据存到空位置里。

(注意:如果哈希表的表长不是4k+3的形式,那么可能会出现哈希表存在空位置但数据插不进去的可能。这里不做赘述了。)

(3)伪随机探测法

index=(Hash(key)+d) mod m

增量序列d使用一个伪随机函数来产生一个落在闭区间[1,m-1]的随机序列。

换言之,确定好一个伪随机函数,那么d这个序列每一项的值就是确定的,只不过可能彼此之间没什么关系,但是只要伪随机函数是确定的,那么它产生的序列就是唯一的。

(正是因为伪随机函数的这个特性,所以常应用在既想得到比较随机的序列,又想使这一随机过程可以复现的场景。这句如果不理解可跳过,浅浅拓展)

2.再哈希法

indexi=Hashi(key)

只不过,这里的Hashi(key)可以认为是一个哈希函数序列。对于一个key,先算Hash1(key),如果发生冲突,就接着算Hash2(key),直到不发生冲突时,再把数据存入。

3.链地址法(拉链法)

之前提到过,如果一开始在索引为i的地方存了数据,之后如果另一个数据也要存在这个位置,就会发生哈希冲突,因为数组的一个位置不能存两个数据。但是,如果强行就是要让多个数据存在一个位置可以吗?

这可以通过邻接表来实现(回想图的存储结构)。

只需要让数组的每个索引的位置上存储一个链表的头指针,然后所有的数据都存到链表里。即某索引位置上如果新来了一个数据,那先把数据放到一个结点里,再把这个结点就用头插法或者尾插法插到链表里,这样就可以避免哈希冲突。

4.公共溢出区法

除了哈希表之外,再建一个新的顺序表,成为溢出表。

当某个数据发生了哈希冲突,就不存到哈希表里了,直接按顺序存到溢出表里即可。

查找时,根据key计算Hash(key),即索引,然后到索引位置检查是否有数据,若没有,说明查询失败,若有数据,就检查这个数据的关键字是不是key,如果是则查询成功,不是则说明可能在构造哈希表时这里发生了哈希冲突,于是直接去溢出表里顺序查找。

注意,公共溢出区法不是一个孤立的办法,也可以和其他方法结合,比如当使用线性探测法把哈希表的所有位置都插满之后,剩下的数据没处可插,就可以放到溢出表里。

好了,以上就是哈希表的主要知识点了,欢迎各位纠错指正!

祝你学会~

标签:知识点,Hash,哈希,索引,可速通,关键字,key,数据
From: https://blog.csdn.net/FulcrumAC/article/details/144246430

相关文章

  • 2024/12/3 【哈希表】 LeetCode 242.有效的字母异位词 【x】
    题目链接:https://leetcode.cn/problems/valid-anagram/description/解法1:classSolution:defisAnagram(self,s:str,t:str)->bool:record=[0]*26foriins:record[ord(i)-ord('a')]+=1fori......
  • 2024/12/3 【哈希表】
    https://www.programmercarl.com/%E5%93%88%E5%B8%8C%E8%A1%A8%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html#%E5%B8%B8%E8%A7%81%E7%9A%84%E4%B8%89%E7%A7%8D%E5%93%88%E5%B8%8C%E7%BB%93%E6%9E%84哈希表(Hashtable)也称为散列表。一.哈希表是什么?哈希表是根据关键码的值而直接......
  • 哈希表
    1.定义哈希表(Hashtable),又称散列表,是一种高效的数据结构,它通过哈希函数将键(key)映射到值(value),实现快速的元素查询、添加和删除操作看完它的定义后,第一个想到的是python的字典,学完后,确实是python的字典......
  • 搞定leetcode面试经典150题之哈希算法
    系列博客目录搞定leetcode面试经典150题之哈希算法搞定leetcode面试经典150题之双指针搞定leetcode面试经典150题之滑动窗口文章目录系列博客目录理论知识1.哈希函数(HashFunction)2.哈希表(HashTable)通过HashMap实现3.哈希算法的应用4.哈希算法的时间复杂度编......
  • 从实战的角度分析渗透测试究竟需要学习了解的知识点,黑客技术零基础入门到精通教程建议
    前言最近有很多人询问,自己明明OWASPTop10都学的差不多了,各种靶场也复现的差不多了,Burpsuite、goby、awvs、dirsearch等等工具也是用的丝滑,但为什么就是感觉挖不到洞呢基础知识已经准备的差不多了,现在可能缺乏的是挖洞时间的思路,针对特定场景下的渗透套路,这个一般可以学......
  • css预处理器sass知识点
    下面一段代码是项目中全局样式的一段代码,解释下作用/**定义一个变量*/$me-button-bgcolor:(primary:$mybutton,warning:"#fff",);/***@each是一个Sass指令,用于循环遍历列表或映射。$key,$colorin$me-button-bgcolor表示将要遍历名为$me-button-bgcol......
  • JVM面试知识点1
    内存结构(掌握内存结构划分、熟知各区域结构功能)经典的JVM内存结构:按照线程是否共享来划分:Heap(堆区)1.堆区的介绍堆是OOM故障最主要的发生区域。它是内存区域中最大的一块区域,被所有线程共享。存放的是实例化的对象信息;Java堆是垃圾收集器管理的主要区域,因此很多......
  • 6张图详解计算机网络知识点,从零基础到精通,收藏这篇就够了!
    一、计算机网络概述1.1计算机网络的分类按照网络的作用范围:广域网(WAN)、城域网(MAN)、局域网(LAN);按照网络使用者:公用网络、专用网络。1.2计算机网络的层次结构TCP/IP四层模型与OSI体系结构对比:1.3层次结构设计的基本原则各层之间是相互独立的;每一层需要有足够的......
  • 哈希的刷题奇遇记2
    [USACO09OCT]BarnEchoesG题目入口题目描述Thecowsenjoymooingatthebarnbecausetheirmoosechoback,althoughsometimesnotcompletely.Bessie,evertheexcellentsecretary,hasbeenrecordingtheexactwordingofthemooasitgoesoutandret......
  • hot100-一刷-01哈希(共3道题)
    1.两数之和题目链接题目描述代码实现分析:暴力的话就是两个for循环依次寻找相加为target的两个数。用一个map记录已经遍历过的数,其中key就用这个数的字面值,value就存它的下标。判断是否相加为taget的时候,只需要看map中是否有target-nums[i]就可以,说明当前的nums[i]和之前......