• 哈希表学习


    学习摘抄

    链接:哈希表-知乎 强推

    定义:

    散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。通过关键码值映射到表中一个位置来访问记录,以加快查找的速度。是根据键(Key)而直接访问在内存存储位置的数据结构。

    哈希函数:

    给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。映射函数称做散列函数,存放记录的数组称做散列表

    总结:

    • 哈希表是一种数据结构
    • 哈希表表示了关键码值记录的映射关系
    • 哈希表可以加快查找速度
    • 任意哈希表,都满足有哈希函数f(key),代入任意key值都可以获取包含该key值的记录在表中的地址

    简单的来说,哈希表是一种表结构,我们可以直接根据给定的key值计算出目标位置。然后就可以读取这个位置的记录。在工程中这一表结构实现通常采用数组。

    在列表查找中,使用最广泛的二分查找算法,复杂度为O(log2n),但其始终只能用于有序列表普通无序列表只能采用遍历查找,复杂度为O(n)。而拥有较为理想的哈希函数实现的哈希表,对其任意元素的查找速度始终为常数级,即O(1)

    可以根据一个key值来直接访问数据,因此查找速度快。本质上是一个数组,因为数组查询效率高,它的底层实现是用到了数组。哈希实现方式:

    1、数组+链表:链表解决哈希冲突

    2、数组+二叉树

    不同于数组,哈希存放的是键值对。

    举例子

    王二 13562389754

    王二是 key 取得 W 就是散列函数 W 的位置就是数据存放的位置 手机号就是 value。

    哈希表就是通过将关键值也就是key通过一个散列函数加工处理之后得到一个值,这个值就是数据存放的位置,我们就可以根据这个值快速的找到我们想要的数据

    这个时候再来个王三怎么办,这个就是哈希冲突!

    键值对我们称作 Entry = { key, value }         key--> 散列函数 --> 存储位置

    Entry 是要存放在数组里面的内容 存储位置就是数组下标。

    通俗讲:数组中1的位置存放的是一个Entry,它不是一个简单的单个数值,而是一个键值对,也就是存放了key和value,key就是学号101011,value就是张三,我们经过哈希函数计算得出的1只是为了确定这个Entry该放在哪个位置而已。

    如果还有人姓 wang 或者 学好是 1 开头呢?

    哈希冲突

    哈希冲突的解决办法

    开放寻址法

    这里所说的开放寻址法其实简单来说就是,既然位置被占了,那就另外再找个位置不就得了,怎么找其他的位置呢?这里其实也有很多的实现,我们说个最基本的就是既然当前位置被占用了,我们就看看该位置的后一个位置是否可用,也就是1的位置被占用了,我们就看看2的位置,如果没有被占用,那就放到这里呗,当然,也有可能2的位置也被占用了,那咱就继续往下找,看看3的位置,一次类推,直到找到空位置。

    拉链法

    拉链法也是比较常用的,像之前你说的HashMap就是使用了这种方法。之前说的开放寻址法采用的方式是在数组上另外找个新位置,而拉链法则不同,还是在该位置,可是,该位置被占用了咋整,总不能打一架,谁赢是谁的吧 ,当然不是这样,这里采用的是链表,什么意思呢?就像图中所示,现在张三和李四都要放在1找个位置上,但是张三先来的,已经占了这个位置,待在了这个位置上了,那李四呢?解决办法就是链表,这时候这个1的位置存放的不单单是之前的那个Entry了,此时的Entry还额外的保存了一个next指针,这个指针指向数组外的另外一个位置,将李四安排在这里,然后张三那个Entry中的next指针就指向李四的这个位置,也就是保存的这个位置的内存地址,如果还有冲突,那就把又冲突的那个Entry放在一个新位置上,然后李四的Entry中的next指向它,这样就形成了一个链表。

    这个拉链法啊,如果冲突的很多,那这个增加的链表岂不是很长,这样也不咋好吧?如果冲突过多的话,这块的链表会变得比较长,怎么处理呢?这里举个例子吧,拿java集合类中的HashMap来说吧,如果这里的链表长度大于等于8的话,链表就会转换成树结构,当然如果长度小于等于6的话,就会还原链表。以此来解决链表过长导致的性能问题。这样设计是因为中间有个7作为一个差值,来避免频繁的进行树和链表的转换,因为转换频繁也是影响性能的啊。

    哈希扩容

    可以解决刚刚说的一直开放寻址法占满的问题。还有一个很重要的原因就是当哈希表被占的位置比较多的时候,出现哈希冲突的概率也就变高了,所以很有必要进行扩容。

    负载因子:负载因子达到一定的比例,也就是被占的位置占总位置的百分比之后,就扩容,一般扩容就是创建一个数组是原来的2倍,再重新hash一遍,因为一般扩容之后 hash函数会变化。

    哈希读取数据

    先通过 key 经过hash函数的处理找到存储位置,然后在存储位置查看 key 是不是我们要找的 不是就根据 next 指针或者开放寻址法的下一个位置 查看 key 是不是我们要找的

    核心

    哈希函数

    直接定址法,数字分析法,折叠法,随机数法和除留余数法等等

  • 相关阅读:
    Rouge安装问题
    解锁ESP32-C3 精简版的 GPIO11
    APK反编译工具汇总
    megahit源码迁移解析
    Python编程 元组中不允许的操作
    【VSCode】代码高亮的调整
    SpringBoot+Vue实现的高校图书馆管理系统 附带详细运行指导视频
    停止从 Kaggle 下载数据集(如果你不是初学者)
    SCS【11】单细胞ATAC-seq 可视化分析 (Cicero)
    【广州华锐互动】利用VR开展建筑塔吊安全操作学习的好处?
  • 原文地址:https://blog.csdn.net/weixin_43389008/article/details/128062020