哈希表

哈希表是将键映射到数组下标,这样可以根据键值快速定位到数组中的元素。
哈希表要注意两点,首先是哈希函数,还有就是散列冲突。

哈希函数

哈希查找第一步就是使用哈希函数将键映射成索引。如果我们有一个保存0-M数组,那么我们就需要一个能够将任意键转换为该数组范围内的索引(0~M-1)的哈希函数。哈希函数需要易于计算并且能够均匀分布所有键.哈希函数最常用的方法就是除留余数法,即对于大小为素数M的数组,对于任意正整数k,计算k除以M的余数。M一般取素数。

避免哈希冲突

通过哈希函数,我们可以将键转换为数组的索引(0-M-1),但是对于两个或者多个键具有相同索引值的情况,我们需要有一种方法来处理这种冲突。
一种比较直接的办法就是,将大小为M的数组的每一个元素指向一个条链表,链表中的每一个节点都存储散列值为该索引的键值对,这就是拉链法。下图很清楚的描述了什么是拉链法。

哈希表_第1张图片

该方法的基本思想就是 选择足够大的M,使得所有的链表都尽可能的短小,以保证查找的效率.

线性探测法是开放寻址法解决哈希冲突的一种方法,基本原理为,使用大小为M的数组来保存N个键值对,其中M>N,我们需要使用数组中的空位解决碰撞冲突。如下图所示:

哈希表_第2张图片

对照前面的拉链法,在该图中,”Ted Baker” 是有唯一的哈希值153的,但是由于153被”Sandra Dee”占用了。而原先”Snadra Dee”和”John Smith”的哈希值都是152
的,但是在对”Sandra Dee”进行哈希的时候发现152已经被占用了,所以往下找发现153没有被占用,所以存放在153上,然后”Ted Baker”哈希到153上,发现已经被占用了,所以往下找,发现154没有被占用,所以值存到了154上。
开放寻址法中最简单的是线性探测法: 当碰撞发生时即一个键的散列值被另外一个键占用时,直接检查散列表中的下一个位置即将索引值加1,这样的线性探测会出现三种结果:

  • 命中,该位置的键和被查找的键相同
  • 未命中,键为空
  • 继续查找,该位置和键被查找的键不同。

线性探查(Linear Probing)方式虽然简单,但是有一些问题,它会导致同类哈希的聚集。在存入的时候存在冲突,在查找的时候冲突依然存在。
性能分析
我们可以看到,哈希表存储和查找数据的时候分为两步,第一步为将键通过哈希函数映射为数组中的索引, 这个过程可以认为是只需要常数时间的。第二步是,如果出现哈希值冲突,如何解决,前面介绍了拉链法和线性探测法下面就这两种方法进行讨论:
对于拉链法,查找的效率在于链表的长度,一般的我们应该保证长度在M/8M/2之间,如果链表的长度大于M/2,我们可以扩充链表长度。如果长度在0M/8时,我们可以缩小链表。
对于线性探测法,也是如此,但是动态调整数组的大小需要对所有的值从新进行重新散列并插入新的表中。
不管是拉链法还是散列法,这种动态调整链表或者数组的大小以提高查询效率的同时,还应该考虑动态改变链表或者数组大小的成本。

你可能感兴趣的:(哈希表)