数据结构与算法学习--哈希

在这里插入图片描述
一、散列冲突的解放方法?
1.常用的散列冲突解决方法有2类:开放寻址法(open addressing)和链表法(chaining)
2.开放寻址法(参考链接
①核心思想:如果出现散列冲突,就重新探测一个空闲位置,将其插入。
②线性探测法(Linear Probing):
插入数据:当我们往散列表中插入数据时,如果某个数据经过散列函数之后,存储的位置已经被占用了,我们就从当前位置开始,依次往后查找,看是否有空闲位置,直到找到为止。
查找数据:我们通过散列函数求出要查找元素的键值对应的散列值,然后比较数组中下标为散列值的元素和要查找的元素是否相等,若相等,则说明就是我们要查找的元素;否则,就顺序往后依次查找。如果遍历到数组的空闲位置还未找到,就说明要查找的元素并没有在散列表中。
删除数据:为了不让查找算法失效,可以将删除的元素特殊标记为deleted,当线性探测查找的时候,遇到标记为deleted的空间,并不是停下来,而是继续往下探测。
结论:最坏时间复杂度为O(n)
③二次探测(Quadratic probing):线性探测每次探测的步长为1,即在数组中一个一个探测,而二次探测的步长变为原来的平方。
④双重散列(Double hashing):使用一组散列函数,直到找到空闲位置为止。
⑤线性探测法的性能描述:
用“装载因子”来表示空位多少,公式:散列表装载因子=填入表中的个数/散列表的长度。
装载因子越大,说明空闲位置越少,冲突越多,散列表的性能会下降。
3.链表法(更常用)
插入数据:当插入的时候,我们需要通过散列函数计算出对应的散列槽位,将其插入到对应的链表中即可,所以插入的时间复杂度为O(1)。
查找或删除数据:当查找、删除一个元素时,通过散列函数计算对应的槽,然后遍历链表查找或删除。对于散列比较均匀的散列函数,链表的节点个数k=n/m,其中n表示散列表中数据的个数,m表示散列表中槽的个数,所以是时间复杂度为O(k)。
代码实现可以参考github地址(参考linux内核hashtab实现):https://github.com/jin13417/algo/tree/master/c-cpp/18_hash/listhash

二、如何设计一个工业级的散列函数
如何设计一个可以应对各种异常的工业级别的散列表,来避免在散列冲突的情况下,散列表的性能急速下降。我们从以下几个方面来考虑:
在这里插入图片描述

三、为什么散列表和链表实现LRU缓存淘汰算法
我们可以利用散列表和双向链表来实现LRU缓存淘汰算法,将时间复杂度降低0(1)。
下面是我用C语言实现Java LinkedHashMap的功能LRU缓存淘汰算法的功能。
双向链表的实现可以直接使用内核的函数:linux内核源码双向链表实现include/linux/list.h
具体结构体定义和实现可以参考查看github代码https://github.com/jin13417/algo/tree/master/c-cpp/19_Dlisthash

四 哈希算法的应用
在这里插入图片描述
1、安全加密:

四、思考
1.Word文档中单词拼写检查功能是如何实现的?
字符串占用内存大小为8字节,20万单词占用内存大小不超过20MB,所以用散列表存储20万英文词典单词,然后对每个编辑进文档的单词进行查找,若未找到,则提示拼写错误。
2.假设我们有10万条URL访问日志,如何按照访问次数给URL排序?
字符串占用内存大小为8字节,10万条URL访问日志占用内存不超过10MB,通过散列表统计url访问次数,然后用TreeMap存储散列表的元素值(作为key)和数组下标值(作为value)
3.有两个字符串数组,每个数组大约有10万条字符串,如何快速找出两个数组中相同的字符串?
分别将2个数组的字符串通过散列函数映射到散列表,散列表中的元素值为次数。注意,先存储的数组中的相同元素值不进行次数累加。最后,统计散列表中元素值大于等于2的散列值对应的字符串就是两个数组中相同的字符串。
1、链式hash实现
2、开地址hash实现
参考文章:

[1] http://www.nowamagic.net/academy/detail/3008086

[2] http://blog.csdn.net/freetourw/article/details/53493616

[3] http://blog.chinaunix.net/uid-27213819-id-3794127.html

猜你喜欢

转载自blog.csdn.net/jsh13417/article/details/83796059
今日推荐