手动实现LRU-最近最少缓存淘汰算法

224

什么是LRU

L=last,R=recently,所以是“最近最少”,在双向链表中实现,最近指的是最近使用的节点,要移动到链表的head,最少指的是最少使用的,要移动到链表的尾部并淘汰(因为容量有限)

实现LRU,需要完成

  • 新添加的数据移动到头部
  • 缓存命中的数据移动到头部
  • 当链表满的时候,移除尾部的数据

一图胜千言

手动实现

  • 数据结构需要通过双向链表来记录缓存的位置关系,哈希表来记录每个节点对应的key和节点本身
  • 方法实现
    • get方法,如果map中存在该key,则返回对应值
    • put方法,如果缓存中不存在,则将node添加到头部;如果缓存存在,则替换该缓存,将node移动到头部并删除尾部

附代码

#include <iostream>
#include <unordered_map>
#include <string>

using namespace std;

/**
 * 实现lru最近最少缓存淘汰算法,最近指的是最近使用的要移动到链表的头部,最少使用的要移到链表的尾部,淘汰尾部
 * 可利用map来映射键值对
 * @return
 */
struct LinkedNode {
    int key, value;
    LinkedNode *prev;
    LinkedNode *next;

    LinkedNode() : key(0), value(0), prev(nullptr), next(nullptr) {}

    LinkedNode(int _key, int _value) : key(_key), value(_value), prev(nullptr), next(nullptr) {}
};

class LruCache {
private:
    LinkedNode *head;
    LinkedNode *tail;
    int size, capacity;
    unordered_map<int, LinkedNode *> map;
public:
    LruCache(int _capacity) : capacity(_capacity), size(0) {
        head = new LinkedNode();
        tail = new LinkedNode;
        head->next = tail;
        tail->prev = head;
    }

    /**
     * 获取某个节点并将其移动到head
     * @param key
     * @return
     */
    int get(int key) {
        if (!map.count(key)) {
            return -1;
        }
        LinkedNode *node = map[key];
        moveToHead(node);
        return node->value;
    }

    void put(int key, int value) {
        if (map.count(key)) {
            LinkedNode *node = map[key];
            node->value=value;
            moveToHead(node);
        } else {
            auto *newNode = new LinkedNode(key, value);
            map[key]=newNode;
            addToHead(newNode);
            ++size;
            if (size > capacity) {
                LinkedNode *pNode = removeTail();
                map.erase(pNode->key);
                delete pNode;
                --size;
            }
        }
    }

    /**
     * 思路:只能根据head的指针来移动
     * @param node
     */
    void addToHead(LinkedNode *node) {
        node->prev = head;
        node->next = head->next;
        head->next->prev = node;
        head->next = node;
    }

    void removeNode(LinkedNode *node) {
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }

    void moveToHead(LinkedNode *node) {
        removeTail();
        addToHead(node);
    }

    LinkedNode *removeTail() {
        LinkedNode *node = tail->prev;
        removeNode(node);
        return node;
    }

    void print(string str) {
        cout<<str<<endl;
        for (auto & i : map) {
            cout<<i.first<<", "<<i.second->value<<endl;
        }
    }
};

int main() {
    auto *lruCache = new LruCache(2);
    lruCache->put(0, 2);
    lruCache->put(1, 6);
    lruCache->put(2, 8);
    lruCache->print("--------new add---------");
    lruCache->put(2, 5);
    lruCache->print("--------old add---------");
}