LRU 算法的全程是Least Recently Used的缩写,即最近最少使用,是一种常见的页面置换算法。redis的内存淘汰策略中,就有使用LRU 算法。
而之所以出现LRU算法,是因为系统中资源是有限的,不可能无限的冗余数据。所以只能保存最频繁访问的数据,来加速数据的访问。
要保证数据访问的时间复杂度是O(1), 在常见的数据结构中,很容易想到HashMap。对于hash map 的实现各个语言都有不同的策略,这里就不展开来讲了。
因为需要频繁地新增、移除数据,在数据结构中,数组肯定是不满足要求,因为数组中的元素是顺序排列的,一旦移除数非头尾的元素,会造成数组索引重排,对性能影响很大。
并且每次找数据都需要遍历数组,明显在时间复杂度上,不满足需求。那么很容易就会想到链表。
双向链表的 每一个元素 都标记了前一个元素与后一个元素。而单向链表只标注了下一个元素。双向链表兼顾了单向链表的优点,可以从任意一个节点进行随机访问,性能非常高。
LRU 算法抽象来讲,只暴露两个方法:Get(key) Set(Key,value)
当Get 时,首先从HashMap中获取Key,然后将双向链表中,当前Node 节点进行解引用,并且将Node 节点转移至链表的头部。
当Set 时,如果HashMap 中已经存在该Key, 择首先执行移动Node 的操作。
如果不存在,择判断链表长度是否已经超过限制,如果超过限制。择移除链表尾部的元素,并在链表的头部插入元素。
实际在使用过程中,因为多线程对map的访问,会引起数据竞争。所以还要通过使用锁或者信号机制,来避免同时写入key。
具体的实现过程可以参考代码:https://github.com/roverliang/leetcode/blob/master/algorithm/lru/lru.go