📜  为 LRU Cache 设计数据结构(1)

📅  最后修改于: 2023-12-03 15:21:35.383000             🧑  作者: Mango

LRU Cache 数据结构

什么是 LRU Cache

LRU Cache 是 Least Recently Used(最近最少使用)缓存的缩写。LRU Cache 是一种缓存策略,通过删除最近最少使用的条目来腾出空间。

使用 LRU Cache 的目的是在需要缓存数据时,保留最常使用的数据,同时删除不常使用的数据,保持缓存的大小不变。这样可以提高性能,减少内存资源的浪费。

LRU Cache 的数据结构
哈希表与双向链表

实现 LRU Cache 的数据结构通常由哈希表和双向链表组成。其中哈希表用于快速查找数据,双向链表用于维护数据的顺序。

哈希表中存储的数据结构为:

class Node {
public:
    int key, val;
    Node* prev;
    Node* next;
    Node(int _key, int _val):key(_key), val(_val), prev(nullptr), next(nullptr){}
};

其中 key 表示节点的键,val 表示节点的值,prev 和 next 分别表示前驱和后继节点指针。

双向链表的数据结构为:

class DoubleLinkedList {
private:
    Node* head;
    Node* tail;
    int size;
public:
    DoubleLinkedList():size(0){
        head = new Node(0, 0);
        tail = new Node(0, 0);
        head->next = tail;
        tail->prev = head;
    }
    void moveToTail(Node* node){
        node->prev->next = node->next;
        node->next->prev = node->prev;
        node->next = tail;
        node->prev = tail->prev;
        tail->prev->next = node;
        tail->prev = node;
        size++;    
    }
    Node* removeHead(){
        Node* removeNode = head->next;
        head->next = removeNode->next;
        removeNode->next->prev = head;
        size--;
        return removeNode;
    }
    int getSize(){
        return size;
    }
};

其中 head 和 tail 分别表示双向链表的头结点和尾结点。moveToTail() 方法将一个节点移到表尾,removeHead() 方法删除表头节点,getSize() 方法返回双向链表的长度。

LRU Cache 类的设计

LRU Cache 类的设计如下:

class LRUCache {
private:
    unordered_map<int, Node*> cache;
    DoubleLinkedList linkedList;
    int capacity;
public:
    LRUCache(int capacity): capacity(capacity){}
    int get(int key){
        auto it = cache.find(key);
        if(it == cache.end()){
            return -1;
        }
        linkedList.moveToTail(it->second);
        return it->second->val;
    }
    void put(int key, int value){
        auto it = cache.find(key);
        if(it != cache.end()){
            it->second->val = value;
            linkedList.moveToTail(it->second);
            return;
        }
        if(linkedList.getSize() == capacity){
            cache.erase(linkedList.removeHead()->key);
        }
        Node* node = new Node(key, value);
        cache[key] = node;
        linkedList.moveToTail(node);
    }
};

其中 cache 存储键值与节点的映射关系,linkedList 为双向链表对象,capacity 表示 LRU Cache 的容量。

get() 方法用于获取一个键对应的值,如果存在则将节点移到表尾,如果不存在则返回 -1。

put() 方法用于插入或更新一个键值对。如果键存在,则更新键对应的值,移到表尾,否则插入新节点,如果 LRU Cache 已满则删除表头节点,并从 cache 中删除对应的键。

LRU Cache 的应用场景

LRU Cache 的应用场景很多,比如操作系统中的页表、web 应用程序中的数据缓存等。在实际中,对于访问模式较为集中的应用场景,可以使用 LRU Cache 提高性能,更好地利用系统资源。