标签:存储空间 顺序 let ali 标记 业界 strong actor 插入数据
举个例子:
假如有89名选手参加学校运动会。为了方便记录成绩,每个选手胸前都会贴上自己的参赛号码。这89名选手的编号依次是1到89。
现在希望编程实现这样一个功能,通过编号快速找到对应的选手信息。怎么做呢?
在来改造一下这个例子。
假设校长说,参赛编号不能设置得这么简单,要加上年级、班级这些更详细的信息。
通过上面的例子,可以总结出这样的规律:
int hash(String key) {
// 获取后两位字符
string lastTwoChars = key.substr(length-2, length);
// 将后两位字符转换为整数
int hashValue = convert lastTwoChas to int-type;
return hashValue;
}
再好的散列函数也无法避免散列冲突。常用的散列冲突解决方法有两类,开放寻址法(open addressing)和链表法(chaining)。
开放寻址法的核心思想是,如果出现了散列冲突,就重新探测一个空闲位置,将其插入。
重新探测新的位置一个比较简单的探测方法是线性探测(Linear Probing)。
当往散列表中插入数据时,如果某个数据经过散列函数散列之后,存储位置已经被占用了,就从当前位置开始,依次往后查找,看是否有空闲位置,直到找到为止。
从图中可以看出,散列表的大小为10,在元素 x 插入散列表之前,已经6个元素插入到散列表中。 x 经过 Hash 算法之后,被散列到位置下标为7的位置,但是这个位置已经有数据了,所以就产生了冲突。于是就顺序地往后一个一个找,看有没有空闲的位置,遍历到尾部都没有找到空闲的位置,于是再从表头开始找,直到找到空闲位置2,于是将其插入到这个位置。
查找操作:
删除操作:
二次探测
双重散列
总结一下:
链表法是一种更加常用的散列冲突解决办法,相比开放寻址法,它要简单很多。
如下图所示,在散列表中,每个“桶(bucket) ”或者“槽(slot) ”会对应一条链表,所有散列值相同的元素都放到相同槽位对应的链表中。
当插入的时候,只需要通过散列函数计算出对应的散列槽位,将其插入到对应链表中即可,所以插入的时间复杂度是O(1)。
当查找、删除一个元素时,同样通过散列函数计算出对应的槽,然后遍历链表查找或者删除。
查找或删除操作的时间复杂度跟链表的长度 k 成正比,也就是 O(k)。对于散列比较均匀的散列函数来说,理论上讲, k=n/m,其中 n 表示散列中数据的个数,m 表示散列表中“槽”的个数。
标签:存储空间 顺序 let ali 标记 业界 strong actor 插入数据
原文地址:https://www.cnblogs.com/xiexiandong/p/13363951.html