数据结构与算法--哈希表--JS

Posted 煜成'Studio

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法--哈希表--JS相关的知识,希望对你有一定的参考价值。

数据结构与算法–哈希表–JS

字典主要特点:

一一对应;key不可以重复;key是无序的

哈希表,通常是基于数组实现的,但是相对于数组,有很多优势:

可以快速插入、删除、查找,O(1)的时间级,实际上,只需要几个机器指令即可;
哈希表的速度比树还要快,基本可以瞬间查找到想要的元素;
哈希表相对于树来说编码要容易很多

哈希表相对于数组的一些不足:

数据是没有顺序的,所以不能以一种固定的方式(从小到大)来遍历其中的元素
key是不允许重复的,不能放置相同的key用于保存不同的元素。

哈希表的结构就是数组,但是对下标进行变换,这种变换叫做哈希函数,通过哈希函数可以获取到HashCode。

哈希化:将大数字转化为数组范围内下标的过程称为哈希化。

哈希函数:将单词转为大数字,大数字再进行哈希化的代码实现放在一个函数内,这个函数叫哈希函数

哈希表:最终将数据插入到的这个数组,对整个结构的封装,称之为哈希表

哈希表里面一旦哈希化时下标值产生了冲突,一般采用的方案:

链地址法(拉链法);开放地址法。

链地址法是每个数组单元中存储的不再是单个数据,而是一个链条,这个链条常用的数据结构是数组或者链表。

用数组或者链表的效率差不多,都是需要线性查找。假如数组每个单元内新加入的数据要放在前面的话,使用链表,效率更高一些。

开放地址法主要通过寻找空白的单元格来添加重复的数据。

在寻找空白单元格的时候有三种方法:

线性探测;二次探测;再哈希法

线性探测:线性地查找空白单元格,一旦遇到空的位置就停止,这样的话,如果删除一个数据项时,不可以将这个位置的下标的内容设置为null,因为会影响之后的查询操作,所以如果删除的话,将该位置进行特殊化处理(例如设置为-1),线性探测有个严重的问题,就是聚集。

如果在没有任何数据的时候,插入的是22 23 24 25,那么意味着下标值为 2 3 4 5的位置都有元素,这种一连串填充单元就叫做聚集
聚集会影响哈希表的性能,无论是插入、查询、删除。
解决这个问题,用二次探测

二次探测主要优化的是探测的步长,线性探测可以看做步长为1的探测,x+1,x+2,x+3依次探测

二测探测,对步长做了优化,比如从下标值x开始,x+(1的平方)、x+(2的平方)、x+(3的平方),一次性探测比较长的距离,避免聚集带来的影响。
二次探测的问题:加入再加入32 112 82 2 192,会造成步长不一的一种聚集,影响效率,但这种情况比连续数字的可能性小,
怎么解决每个的步长不一样,使用再哈希法
产生一种依赖关键字的探测序列,而不是每个关键字都一样。不同关键字即使映射到相同的数组下标,也可以使用不同的探测序列。

再哈希法的做法是:把关键字用另一个哈希函数再做一次哈希化,用这次哈希化的结果作为步长。

对于指定的关键字,步长在整个探测中是不变的,不过不同的关键字使用不同的步长。
第二次哈希化需要具备的特点:1.和第一个哈希函数不同;2.不能输出为0
哈希函数为stepSize = constant - (key % constant),其中constant是质数且小于数组的容量。

哈希表中执行插入和搜索操作效率是非常高的,如果没有冲突,那么效率就会更高。发生冲突,存取时间就依赖后来的探测长度。
平均探测长度以及平均存取时间,取决于填装因子,随着填装因子变大,探测长度也越长。

填装因子:表示当前哈希表中包含的数据项和整个哈希表长度的比值。填装因子=总数据项/哈希表长度。

开放地址法的填装因子为1;链地址法的填装因子可以大于1。

效率

二次探测和在哈希探测的性能比线性探测略好,都是指数方式进行增长;链地址法是比较平缓的,成线性增长。
随着填装因子变大,效率下降的情况在不同开放地址法方案中比链地址法更严重。在开发中,使用链地址法的情况更多一些。

哈希表的主要优点就是速度

好的哈希表应该具备两个优点:快速的计算;均匀的分布

快速的计算:调高速度的一个办法就是让哈希函数中尽量少的有乘法和除法,因为乘除的效率是比较低的。
均匀的分布:无论是链地址法还是开发地址法,当多个元素映射到同一个位置的时候,都会影响效率,所以需要让元素在哈希表中均匀的分布。

快递计算:霍纳法则/秦九昭算法,时间复杂度O(n)
均匀分布:在使用常量的地方,尽可能使用质数。比如哈希表的长度,n次幂的底数。

Java中的哈希表采用的是链地址法,HashMap为了提高效率,采用的是位运算方式,index=HashCode(Key)&(Length - 1),相对于取模运算来说性能是高的。

//设计哈希函数
//1.将字符串(第一个参数)转成为比较大的数字hashCode
//2.将大的数字hashCode压缩到数组范围(大小,第二个参数)之内
function hashFunc(str, size) {
    //定义hashCode变量
    var HashCode = 0;
    //霍纳算法,计算hashCode的值
    //charCodeAt是把字符转为Unicode编码
    for (var i = 0; i < str.length; i++) {
        HashCode = 37 * HashCode + str.charCodeAt(i); //37是经常使用的质数
    }
    //取余操作
    var index = HashCode % size;
    return index;
}

//测试哈希函数
alert(hashFunc('abc', 7));

以上是关于数据结构与算法--哈希表--JS的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript笔试题(js高级代码片段)

数据结构与算法各种字符串Hash函数比较

Java数据结构与算法——哈希表

Java数据结构与算法——哈希表

手撸golang 基本数据结构与算法 哈希表

HashMap原理:哈希函数的设计