散列表+双向链表
1、上一篇讲到使用链表法来解决散列冲突,是单链表。这一节为散列表+双向链表的使用。但该双向链表与散列表无任何关系,与链表法中的单链表不一样,散列表+双向链表只是将两种数据结构结合在一起,用于更高效的查询、插入和删除。
2、图中可以看出,双向链表中有四个指针,data存储数据,prev、next分别为前驱和后继指针,将结点串在双向链表中,保证双向链表的有序性,提高插入和删除的性能。
hnext表示将结点串在散列表的拉链中,作用类似于链表法中的单链表的 next指针,即用于解决散列冲突
即,prev和next表示串联的是整个双向链表(纵向),hnext串联的是散列表中的该条拉链表(横向)。
3、应用——LRU
<1>LRU算法:最近最长时间未使用缓存淘汰算法。使用散列表+双向链表的数据结构实现LRU算法,对缓存数据的操作主要为查找、删除、插入。
<2>查找:先计算散列值,在散列表中进行查找,此时间复杂度为O(1)。然后将该数据移动到双向链表的尾部,注意此操作不会改变节点在双向链表中的位置,只改变next指针的指向,将原本的尾结点的next指针指向该节点,再将该节点的next指针指向Null即可。
<3>删除:通过散列表找到要删除的节点后,在双向链表中删除该节点。因双向链表可以直接获取前驱指针,因此删除的时间复杂度也为O(1)
<4>插入:若插入的数据已在缓存中则移动到双向链表尾部。若不在且空间未满时,直接插入到双向链表尾部;若不在且空间已满时,则删除头结点,插入双向链表尾部。
<5>JAVA中的LinkedHashMap的实现原理同LRU
<6>Redis的有序集合则使用了散列表+跳表
哈希算法
1、哈希算法:将任意长度的二进制串映射为固定长度的二进制值串,产生的哈希值长度是固定且有限的。有限是因为表示二进制的位数是有限的,根据鸽巢原理(10个巢,11个鸽子,必定有一个巢内有大于一只鸽子),必然会有哈希值下相同的情况。当然哈希算法产生的哈希值越长,散列冲突的概率也越低,但是计算时间也很长。
2、哈希算法的应用:
(1)安全加密算法:MD5、SHA
(2)唯一标识:对大数据进行信息摘要
(3)数据校验
(4)散列函数
(5)负载均衡
(6)数据分片
(7)分布式存储
其中5、6、7的原理是将key值进行哈希,将哈希值与服务器个数进行取模,得到的值即为存储该key的服务器编号
则有问题:若服务器不够,则要进行服务器扩容,之前计算的服务器编号则要全部重新计算。因此一致性哈希算法可以解决此问题(建议单独百度该算法,不做详解)
图源:王争——数据结构与算法之美