什么是散列表:
散列表用的是数组支持按照下标随机访问数据的特性,所以散列表其实就是数组的一种扩展,由数组演化而来。可以说,如果没有数组,就没有散列表。
原理:
散列表用的就是数组支持按照下标随机访问的时候时间复杂度是0(1)的特性。我们通过散列函数把元素的键值映射为下标,然后将数据存储在数组中对应下标的位置。当我们按照键值查询元素时,我们用同样的散列函数,将键值转化数组标标,从对应的数组下标的位置取数据。
散列表中的一些概念:
哈希函数:通过把关键字key映射到数组中的一个位置来访问记录。映射过程通过函数实现,而这个函数就叫散列函数(哈希函数),存放关键字的数组称为散列表。
键(key)或者关键字: 哈希算法处理的值。
哈希值(散列值):将key通过算法计算得到的值。
装在因子: 散列表中一定比例的空闲槽位。公式: 散列表的装载因子 = 填入表中的元素个数 / 散列表的长度
装载因子越大,说明空闲位置越少,冲突越多,散列表的性能会下降。
散列函数
散列函数,顾名思义,它是一个函数。我们可以把它定义成 hash(key),其中 key 表示元素的键值,hash(key) 的值表示经过散列函数计算得到的散列值。
设计要求:
- 散列函数计算得到的散列值是一个非负整数;.
- 如果key1 = key2,那hash(key1) == hash(key2);
- 如果key1 != key2,那hash(key1) != hash(key2),
- 散列函数的设计不能太复杂,散列函数生成值要尽可能随机并且均匀分布
如果不符合3 那么就出现了散列冲突,散列冲突是无法避免的
散列冲突
如果key1 != key2,那hash(key1) == hash(key2)就叫做散列冲突。
开放寻址法:如果出现了散列冲突,我们就重新探测一个空闲位置,将其插入。
当我们往散列表中插入数据时,如果某个数据经过散列函数散列之后,存储位置已经被占用了,我们就从当前位置开始,依次往后查找,看是否有空闲位置,直到找到为止。
链表法:
链表法是一种更加常用的散列冲突解决办法,相比开放寻址法,它要简单很多。我们来看这个图,在散列表中,每个"桶(bucket) "或者"槽(slot) "会对应一条链表,所有散列值相同的元素我们都放到相同槽位对应的链表中。