前言
这是「算法导论」系列文章的第一篇,所以我先简单说明一下;这个系列主要针对算法这一块做一个整理,而学习过程中,主要使用算法导论作为参考书籍,通过 C语言进行编码测试,题库来源主要是leetcode;
但是因为「算法导论」涉及较多的数学概念,导致比较难懂,所以更新的时间和频率会出现不确定性,请大家谅解;以及基础的栈, 队列等概念不做详细的讲解,因为其实质就是对数据插入和数据删除做了一定限制的数据结构,后续会根据具体习题进行讲解
基本概念
-
散列表定义
散列表是根据键而直接访问在内存存储位置的数据结构,其实质是对普通数组概念的扩展,其目标是使数据的查询效率达到O(1)
-
散列函数
wiki上是这样解释散列函数的:散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值的指纹;其实质就是通过一个固定长度数据去表达元数据
-
装载因子
装载因子指散列表装满程度的标志因子,表示为:
填入表中的元素个数 / 散列表的长度;
Java的系统库限制了荷载因子为0.75,超过此值将resize散列表
散列函数
-
直接寻址法
直接寻址法又称为数组,其特点是将元数据值或元数据属性值作为下标进行存储
-
除法散列法
除法散列法指通过取k除以m的余数,将关键字k映射到m个槽中某一个,即散列函数为:
h(k)=k mod m -
乘法散列法
同理得到散列函数为:
h(k)=[m(kA mod 1)]; 指将关键字k乘以常数A并取整得到槽位置 -
全域散列法
全域散列法主要将哈希函数替换成一组哈希函数集合,通过随机化策略进行选择哈希函数进行散列;而为什么要增加随机化选择哈希函数的策略呢?,原因就在于当哈希函数固定时,就一定会有一组数据使得散列表退化成链表;
同时书中还提到了完全散列,有人将其称之为完美散列,其结构就是二级全域散列表
其实对于全域散列法,我一直不理解当策略随机化时,你应该如何将数据找到并取出来呢?这主要是我们对随机策略有误解,随机函数的获取实质是通过进制转换的方式获取,所以只要数据一致实质获取的哈希函数就是一致的
散列冲突
-
链接法
链接法其实很好理解,如果读者对java有接触,就知道HashMap类就利用了这个策略,其实质就是将元素槽改造成链表结构,使得Hash值相同的元素不会产生冲突,而是向链表进行插入操作
-
开放寻址法
开放寻址法主要通过再次探查的方式寻找元素槽,主要包括以下几种:
-
线性探查 - 以冲突位置为基准,向后移动指定位置进行插入
-
二次探查 - 以冲突位置为基准,向后移动二次方数量位置进行插入
-
双重散列 - 以冲突位置为基准,向后移动再次哈希位置,得到
h(k,i) = (h1(k)+ih2(k)) mod m