算法基础之哈希表

大家好,这里是教授.F

什么是哈希表:

      哈希表其实就是数组的pro版本。数组有下标,每个下标对应着一个值。哈希表也类似,哈希表有很多哈希值,然后每一个哈希值都会对应着一个值。就是这样:hash(key)

哈希表的要求:

        1.key必须是不变的。

这点非常重要。

所谓不可变类型,就是说这个对象一旦创建,它的值就不能再改变了。比如 Java 中的 String, Integer 等类型,一旦创建了这些对象,你就只能读取它的值,而不能再修改它的值了。

作为对比,Java 中的 ArrayList、LinkedList 这些对象,它们创建出来之后,可以往里面随意增删元素,所以它们是可变类型。

因此,你可以把 String 对象作为哈希表的 key,但不能把 ArrayList 对象作为哈希表的 key。

 如果使用ArrayList对象作为哈希表的key,那经常性的改变会导致每次计算 hashCode 都要遍历整个数组,复杂度是 O(N),这样就会导致哈希表的增删查改操作的复杂度退化成 O(N)

哈希函数:

        我们先来讨论一下,关于哈希表中key的取值我们想怎么来搞定???

我们想怎么根据key来得到一个唯一值。首先在java中每一个对象都有int hashCode()这个方法。在我们不重写hashCode()这个方法的时候,该方法就会返回对象的内存地址,恰恰我们就想拿到一个唯一的数值来进行转换。所以唯一值思路我们已经确定。但还有一个问题:hashCode()的类型是int类型,int可是有负数的啊。索引可不兴是负数啊!如果只是这样想 :

int h = key.hashCode();
h = Math.abs(h);

也是不行。因为int类型得负数 2 ^31 ,正数是 2 ^ 31 - 1。会发现数据溢出了。

               我们可以通过位运算来进行操作,将最高位得符号去掉。这样不仅能解决问题,而且位运算比算术运算快得多。

        

h = h & 0x7fffffff;
h 是一个整数变量,它代表某个哈希值。
0x7fffffff 是一个十六进制常量,
对应的二进制表示是 0111 1111 1111 1111 1111 1111 1111 1111。
它的最高位(即最左边的一位)是 0,其余位都是 1。
& 是按位与(AND)操作符,它将两个操作数的对应位进行逻辑与运算。
只有当两个操作数的对应位都是 1 时,结果位才是 1,否则结果位为 0。

但还有一个问题,就是这个 hashCode 一般都很大,我们需要把它映射成 table 数组的合法索引。

这是使用的技巧是取模。

 h % table.length;
这样就保证了在table数组的范围内
但是考虑性能,我们可以通过位运算来替代:
要将取模运算 h % table.length 用位运算来代替,
通常要求 table.length 是 2 的幂次方。这是因为取模运算可以用位运算来模拟,
而且当除数是 2 的幂次方时,这种替代特别高效。

假设 table.length 是 2 的幂次方,
比如 table.length = 2^k,那么 h % table.length 
可以等价地表示为 h & (table.length - 1)。

具体的替代方法如下:

将 table.length 减去 1,得到一个二进制全是 1 的数,
例如,当 table.length = 16 时,table.length - 1 = 15,
其二进制表示为 0000 0000 0000 1111。
这样得到的结果就是一个与原数相比,位数更低的数字。
比如对于 table.length = 16,它相当于对 h 的低 4 位进行了保留,而将高位清零。
这个结果就等价于对 h 取模 table.length,
因为对于任何整数 x,x % 2^k 的结果就是 x 的低 k 位的值。

 哈希冲突:

        现实中,我们通过这样计算哈希值,但是往往会造成哈希值相同,也就是哈希冲突,所以对于哈希冲突我们还要有解决方案:

以上就是出现情况的解决方案。

扩容和负载因子:

        为什么要引入这个东西,这个东西是什么???

首先上面出现的哈希冲突的解决方案会降低性能。

拉链法:就是在同一个位置使用链表进行串联,在查找的时候,时间复杂度为:O(K)K 是这个链表的长度。

线性探查法:就是如果算出的哈希值位置已经被占了。我们就继续往后面寻找位置,直到找到一个空位置。时间复杂度为:O(K)K 为连续探查的次数。

当哈希表有太多的key-value时,很容易出现冲突的情况,所以我们需要进行扩容。

负载因子(Load Factor)是指哈希表(Hash Table)中已存储元素数量与哈希表容量之比。

负载因子的计算公式也很简单,就是 size / table.length。其中 size 是哈希表里面的 key-value 对的数量,table.length 是哈希表底层数组的容量。

当哈希表内元素数量达到负载因子的时候,table数组就会进行扩容。

  • 35
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值