算法-LRU算法实现

LRU算法即最近最少使用(Least Recently Used)算法,其核心思想是“最近访问的数据会在未来一段时间内仍然被使用,已经很久没有使用的数据大概率在未来较长的一段时间内仍然不会使用”,基于这个思想,衍生出一种缓存淘汰机制,根据缓存历史访问记录来进行数据淘汰,从缓存中寻找久未使用的数据然后置换出来,从而存入新数据。

算法思想:

  LRU算法是一种缓存淘汰机制,首先要解决存储问题,存储可选择数组vector、链表list、哈希表map、集合set等,提取算法执行过程中的关键操作,主要有数据的随机访问、数据的随机更新淘汰,哈希表map可以实现O(1)时间复杂度的随机访问,list可以实现O(1)时间复杂度的随机更新淘汰,因此LRU可以考虑通过哈希表辅以双向链表实现,算法实现如下:

  因为有哈希表的存在,所以每一条数据记录都会以键值对的形式存在,我们用一个哈希表和一个双向链表维护所有在缓存中的键值对。

  • 双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的键值对是最久未使用的。
  • 哈希表即为普通的哈希映射(HashMap),通过缓存数据的键映射到其在双向链表中的位置。

  在数据随机访问时,首先使用哈希表进行定位,找出缓存项在双向链表中的位置(即相关节点),随后将其移动到双向链表的头部,即可在 O(1)O(1) 的时间内完成 get 或者 put 操作。具体的方法如下:

  1.   对于 get 操作,首先判断 key 是否存在:
  2.   如果 key 不存在,则返回 -1;
  3.   如果 key 存在,则 key 对应的节点是最近被使用的节点。通过哈希表定位到该节点在双向链表中的位置,并将其移动到双向链表的头部,最后返回该节点的值。
  4.   对于 put 操作,首先判断 key 是否存在:
  5.   如果 key 不存在,使用 key 和 value 创建一个新的节点,在双向链表的头部添加该节点,并将 key 和该节点添加进哈希表中。然后判断双向链表的节点数是否超出容量,如果超出容量,则删除双向链表的尾部节点,并删除哈希表中对应的项;
  6.   如果 key 存在,则与 get 操作类似,先通过哈希表定位,再将对应的节点的值更新为 value,并将该节点移到双向链表的头部。 
class LRUCache {
private:
    int capacity;
    list<pair<int, int>> recent;
    unordered_map<int, list<pair<int, int>>::iterator> pos;
public:
    LRUCache(int capacity) : capacity(capacity) {}
    
    int get(int key) {
        if (pos.find(key) != pos.end()){
            put(key, pos[key]->second);
            return pos[key]->second;
        }
        return -1;
    }
    
    void put(int key, int value) {
        if (pos.find(key) != pos.end())
            recent.erase(pos[key]);
        else if (recent.size() >= capacity) {
            pos.erase(recent.back().first);
            recent.pop_back();
        }
        recent.push_front({ key, value });
        pos[key] = recent.begin();
    }
};

 

练习题:面试题 16.25. LRU缓存
posted @ 2020-08-17 23:51  斜风戏雨  阅读(325)  评论(0)    收藏  举报