散列表(哈希表)

概述

什么是散列表? 如果说起它的另一个名字, 你一定很熟悉, 它的英文叫"Hash Table", 哈希表, 很熟悉吧.

散列的思想, 其实就是利用数组的随机访问特性, 将key-value形式的数据, 其中的key转换成数组下标, 即可实现将其存放到数组中, 进而实现随机访问.

而其中将key转换成数字的函数, 被称为散列函数, 或哈希函数.

为了方便大家看, 以下统一称为哈希, 知道这俩是一回事就行.

哈希函数#

设计一个哈希函数, 有如下三点要求:

  1. 散列函数计算得出的值是一个正整数(数组下标嘛)

  2. 若key相等, 则计算后的哈希值相等

  3. 若key不相等, 则计算后的哈希值不相等

后面两点, 说白了就是, 计算后的哈希值是唯一的, 不变的.

要想达到第二点要求, 应该不难, 只要算法固定, 输入与输出就应该是固定的嘛

但是, 要想实现第三点, 就不一样了, 要想找到一个完全符合的, 几乎是不可能. 而且, 要想将其放到数组中, 数组的大小是有限的啊, 所以, 当出现计算后两个哈希值相等的情况, 就是哈希冲突.

哈希冲突#

既然没有完美的哈希函数, 那么就不可避免会发生哈希冲突, 那么就要解决哈希冲突, 常用的有开放寻址法和链表法.

1. 开放寻址法

开放寻址法的思想很简单, 当发生哈希冲突的情况时, 就从当前位置往后找, 找到第一个空缺的位置放入.

对于开放寻址法, 查找操作也顺理成章, 计算key的哈希值后, 查看其下标元素是否为要寻找的元素, 若不是, 向后寻找, 一直找到出现空位, 则说明key不在表中.

但是, 删除操作就比较麻烦了, 因为查找是通过空位来判断的, 若直接删除key, 就会在下次查找时出现空位而打断本来应该继续的查找. 对于这种情况, 我们可以将删除的空位标记为delete, 查找时遇到delete不会中断就好了.

上面说的这种查找方法叫线性探测法, 顾名思义, 就是一个一个往后找, 另外还有两种经典查找方法: 二次探测和双重散列.

二次探测: 线性探测每次探测的步长(每次往后找的个数)是1, 也就是说探测的下标是k+0,k+1,k+2,k+3. 二次探测就是每次访问的步长变为原来的二次, 探测的下标为: k+0,k+1,k+2,k+9

双重散列: 就是不单单使用一个哈希函数, 而是使用一串, 当第一个哈希函数发生冲突时, 就用第二个计算, 再冲突, 再用第三个计算, 直到找到空位

问题

很明显, 开放寻址法有很大的问题. 当表中数据越来越多的时候, 哈希冲突的概率也会越来越大, 对应的查找操作也就会越来越慢, 甚至最终会遍历整个表.

装载因子

用装载因子来表示哈希表中空位的多少, 其计算公式是:

装载因子=表中元素个数 / 表的长度

装载因子越大, 说明空位越少, 冲突越多, 哈希表的性能越低.

2. 链表法

使用链表法来解决哈希冲突相对来说更为常见一些, Java中的HashMap就是这么处理的.

通过一张图来简单说明链表的处理方法:

当发生哈希冲突时, 将数据插入到对应的链表中. Java中的HashMap就是通过hashcode方法计算数组下标, 再通过equals方法判断两对象是否相等.

 

posted @   烟草的香味  阅读(361)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示
主题色彩