【算法】 LRU Cache

目录

一、什么是LRU Cache

二、LRU Cache的实现

三、 LRU算法的运用场景


一、什么是LRU Cache

LRU是Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法。 什么是 Cache?狭义的Cache指的是位于CPU和主存间的快速RAM, 通常它不像系统主存那样使用 DRAM技术,而使用昂贵但较快速的SRAM技术。 广义上的Cache指的是位于速度相差较大的两种硬件之间, 用于协调两者数据传输速度差异的结构。除了CPU与主存之间有Cache, 内存与硬盘之间也有Cache,乃至在硬盘与网络之间也有某种意义上的Cache——称为Internet临时文件夹或 网络内容缓存等。

Cache的容量有限,因此当Cache的容量用完后,而又有新的内容需要添加进来时, 就需要挑选并舍弃原有的部分内容,从而腾出空间来放新内容。LRU Cache 的替换原则就是将最近最少使用的内容替换掉。其实,LRU译成最久未使用会更形象, 因为该算法每次替换掉的就是一段时间内最久没有使用过的内容。

二、LRU Cache的实现

实现LRU Cache的方法和思路很多,但是要保持高效实现O(1)的put和get,那么使用双向链表和哈希表的搭配是最高效和经典的。

使用双向链表是因为双向链表可以实现任意位置O(1)的插入和删除,使用哈希表是因为哈希表的增删查改也是O(1);但我们要注意的是哈希表val值存储的并不是对应的数据值,而是指向双向链表某一个节点的迭代器,这样子在我们查找数据在链表中某一个节点位置时,就可以直接通过哈希表来定位,并不用遍历链表了:

该链表的实际意义在于我们在插入数据时会使用头插的方法,每当访问和修改时会将访问到的节点提到链表的头部;如此一来最久未被使用过的数据便是链表尾部的节点,当我们使用带头双向循环链表时对节点的时间复杂度为O(1)

下面我们通过一道OJ题来实现一下该算法:

题目地址:146. LRU 缓存 - 力扣(LeetCode)

解题代码: 

class LRUCache {
public:
    LRUCache(int capacity) : _capacity(capacity) {}

    int get(int key) {
        auto ret = _hashMap.find(key);
        if (ret != _hashMap.end()) {
            auto it = ret->second;
            // 将访问到的节点插入到链表的头部
            _LRUList.splice(_LRUList.begin(), _LRUList,
                            it); // 使用接合(splice)接口可以避免迭代器失效问题
            return (*it).second;
        } else {
            return -1;
        }
    }

    void put(int key, int value) {
        auto ret = _hashMap.find(key);
        if (ret == _hashMap.end()) {
            if (_hashMap.size() == _capacity) {
                // 逐出最久未使用的关键字
                _hashMap.erase(_LRUList.back().first);
                _LRUList.pop_back();
            }
            _LRUList.push_front(make_pair(key, value));
            _hashMap[key] = _LRUList.begin();
        } else {
            auto it = ret->second;
            it->second = value;
            // 将修改到的节点插入到链表的头部
            _LRUList.splice(_LRUList.begin(), _LRUList,
                            it); // 使用接合(splice)接口可以避免迭代器失效问题
        }
    }

private:
    typedef list<pair<int, int>>::iterator LtIter;
    unordered_map<int, LtIter> _hashMap;
    list<pair<int, int>> _LRUList;

    size_t _capacity;
};

三、 LRU算法的运用场景

LRU算法也可以用于一些实际的应用中,如你要做一个浏览器,或类似于淘宝客户端的应用的就要用到这个原理。大家都知道浏览器在浏览网页的时候会把下载的图片临时保存在本机的一个文件夹里,下次再访问时就会直接从本机临时文件夹里读取。但保存图片的临时文件夹是有一定容量限制的,如果你浏览的网页太多,就会一些你最不常使用的图像删除掉,只保留最近最久使用的一些图片。这时就可以用到LRU算法 了,这时上面算法里的这个特殊的栈就不是保存页面的序号了,而是每个图片的序号或大小;所以上面这个栈的元素都用Object类来表示,这样的话这个栈就可以保存的对像了。

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 使用Python编写LRU缓存算法的一个简单实现可以如下:def lru_cache(maxsize): cache = {} queue = [] def wrapper(func): def inner(*args, **kwargs): key = str(args) + str(kwargs) if key in cache: return cache[key] result = func(*args, **kwargs) if len(queue) >= maxsize: del cache[queue.pop(0)] cache[key] = result queue.append(key) return result return inner return wrapper使用Python编写LRU缓存算法的一个简单实现可以如下:def lru_cache(maxsize): cache = {} queue = [] def wrapper(func): def inner(*args, **kwargs): key = str(args) + str(kwargs) if key in cache: return cache[key] result = func(*args, **kwargs) if len(queue) >= maxsize: del cache[queue.pop(0)] cache[key] = result queue.append(key) return result return inner return wrapper答:使用Python编写LRU缓存算法的一个简单实现可以如下:def lru_cache(maxsize): cache = {} queue = [] def wrapper(func): def inner(*args, **kwargs): key = str(args) + str(kwargs) if key in cache: return cache[key] result = func(*args, **kwargs) if len(queue) >= maxsize: del cache[queue.pop(0)] cache[key] = result queue.append(key) return result return inner return wrapper用Python编写LRU缓存算法是可行的,通过使用上面提供的简单实现可以实现这一功能。 ### 回答2: LRU(Least Recently Used)算法是一种常用的缓存替换策略,它根据数据节点的使用顺序来决定是否将数据从缓存中删除。当缓存已满,需要替换一个节点时,LRU算法选择最近最少使用的数据节点进行替换。 下面是通过Python实现一个简单的LRU Cache算法: ```python class LRUCache: def __init__(self, capacity): self.capacity = capacity self.cache = {} self.keys = [] def get(self, key): if key in self.cache: self.keys.remove(key) self.keys.append(key) # 更新使用顺序 return self.cache[key] else: return -1 def put(self, key, value): if key in self.cache: self.keys.remove(key) self.keys.append(key) # 更新使用顺序 self.cache[key] = value else: if len(self.keys) >= self.capacity: # 删除最近最少使用的数据节点 del_key = self.keys[0] del self.cache[del_key] self.keys = self.keys[1:] self.keys.append(key) # 更新使用顺序 self.cache[key] = value ``` 在这个实现中,我们使用一个字典 `cache` 来保存数据节点的键值对,并使用列表 `keys` 来记录数据节点的使用顺序。`capacity` 参数指定了缓存的最大容量。 在 `get` 方法中,如果需要获取的 `key` 存在于缓存中,我们首先从 `keys` 列表中移除 `key`,然后将其添加到列表的末尾,以表示最近使用过。然后返回对应的值。如果 `key` 不存在于缓存中,返回 -1。 在 `put` 方法中,如果需要插入的 `key` 已经存在于缓存中,我们同样要将其移除并重新添加到 `keys` 列表的末尾,并更新对应的值。否则,如果缓存已满,我们删除 `keys` 列表的第一个元素,然后从 `cache` 字典中删除对应的键值对。最后,将新的 `key` 和 `value` 添加到 `keys` 列表和 `cache` 字典中。 这样,我们就实现了一个简单的LRU Cache算法。 ### 回答3: LRU缓存(Least Recently Used,最近最少使用)是一种常用的缓存算法,它根据数据的使用历史来决定哪些数据应该保留在缓存中。当缓存达到最大容量时,如果有新数据要放入缓存中,那么就需要删除最久未使用的数据。 下面是用Python实现LRU缓存算法的示例代码: ```python class LRUCache: def __init__(self, capacity: int): self.capacity = capacity self.cache = {} self.order = [] def get(self, key: int) -> int: if key in self.cache: self.order.remove(key) self.order.append(key) return self.cache[key] else: return -1 def put(self, key: int, value: int) -> None: if key in self.cache: self.order.remove(key) elif len(self.cache) >= self.capacity: oldest_key = self.order.pop(0) del self.cache[oldest_key] self.cache[key] = value self.order.append(key) ``` 以上代码中,LRUCache类是实现LRU缓存算法的主要类。它有三个主要成员变量:capacity表示缓存的容量,cache是一个字典,用于存储缓存数据,order是一个列表,用于记录数据的访问顺序。 `get`方法用于从缓存中获取指定的键对应的值。如果键存在于缓存中,就将该键移到最后,表示最近使用过,然后返回该键对应的值;否则返回-1。 `put`方法用于向缓存中添加新的键值对。如果键已经存在于缓存中,就将该键移到最后,表示最近使用过,然后更新该键对应的值;如果缓存已满,就删除最久未使用的键值对;最后在缓存中添加新的键值对,并将该键添加到order列表的最后。 这样通过LRUCache类,我们可以轻松实现一个LRU缓存,并且保证缓存的容量不会被超出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

1e-12

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值