LRU算法

LRU算法是什么和为什么?

由于计算机缓存空间是有限的,所以要淘汰缓存中不常用的数据,留下常用的数据,腾出空间给新的数据。

LRU是Least recently used的缩写,字面意思是“最近最少使用”,也可理解为“最久未使用”,最久未使用的当然就要被淘汰了。

思路一

使用有序单链表实现。

规定:越靠近链表尾部的结点是越早之前访问的,越靠近链表头部的节点是最近访问的。

当有一个新的数据被访问时,我们从链表头开始顺序遍历链表。

1. 如果此数据之前已经被缓存在链表中了,我们遍历得到这个数据对应的结点,并将其从原来的位置删除,然后再插入到链表的头部。

2. 如果此数据没有在缓存链表中,又可以分为两种情况:

如果此时缓存未满,则将此结点直接插入到链表的头部;

如果此时缓存已满,则链表尾结点删除,将新的数据结点插入链表的头部。

时间复杂度

在缓存中查找某个数据,不管缓存有没有满,都需要遍历一遍链表,所以缓存访问的时间复杂度为 O(n)。

访问一个新数据时,先要查找缓存,再将数据放入缓存,由于查找缓存的原因,所以更新缓存的复杂度为O(n)。

思路二

使用哈希表+链表实现

因为哈希表的查找是很快的,时间复杂度为O(1)。可以将查找和更新缓存的时间复杂度都降为O(1)。

哈希表查找快,但是数据无固定顺序;链表有顺序之分,插入删除快,但是查找慢。所以结合一下,形成一种新的数据结构:哈希链表。

借助哈希表赋予了链表快速查找的特性:可以快速查找某个 key 是否存在缓存(链表)中,同时可以快速删除、添加节点。

为什么要是双向链表,单链表行不行?

因为删除一个链表节点不光要得到该节点本身的指针,也需要操作其前驱节点的指针,而双向链表才能支持直接查找前驱,保证操作的时间复杂度 O(1)。

既然哈希表中已经存了 key,为什么链表中还要存键值对呢,只存值不就行了?

当你删除一个链表节点,同时还要把哈希表中映射到该节点的 key 删除,而这个 key 只能从链表节点得到。如果链表节点结构中只存储 val,那么就无法得知 key 是什么,就无法删除哈希表中的key,造成错误。

使用有序字典来实现LRU算法

Python有个collections.OrderedDict类,是一个有序字典,这样就相当于实现了哈希表+双向链表。

即有O(1)的查找速度,又有O(1)的更新速度。

这样就省去了实现哈希表+双向链表的代码。

Python代码实现

class LRUCache:

    def __init__(self, capacity):
        self.capacity = capacity  # 缓存容量
        self.cache = collections.OrderedDict()  # 缓存,有序字典,代替散列表+链表

    def get(self, key):
        if key not in self.cache:
            return -1  # 访问的数据不在缓存中则返回-1
        value = self.cache.pop(key)  # 将命中缓存的数据移除
        self.cache[key] = value  # 将命中缓存的数据放到头部
        print("get操作后的缓存: ", self.cache)
        return self.cache[key]

    def put(self, key, value):
        if key in self.cache:
            self.cache.pop(key)  # 已经在缓存中,则先移除老的数据
        elif len(self.cache) == self.capacity:
            self.cache.popitem(last=False)  # 不在缓存中并且到达最大容量,则把最后的数据淘汰
        self.cache[key] = value  # 将新数据添加到头部
        print("put操作后的缓存: ", self.cache)

if __name__ == "__main__":
    test = LRUCache(5)
    test.put('a', 1)
    test.put('b', 2)
    test.put('c', 3)
    test.put('d', 4)
    test.put('e', 5)
    print(test.get('a'))
    print(test.get('c'))
    print(test.get('d'))
    print(test.get('e'))
    print(test.get('a'))
    test.put('f', 6)
    print(test.get('b'))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值