【算法】【一般】LRU淘汰算法(C++)

275 阅读3分钟

主要需要记住一些链表的操作

addToHead(DlinkedNode&* node) and removeNode(DLinkedNode* node)

LRU淘汰算法:

在JAVA PYTHON中都有对应的数据结构,如OrderedDict(py)、LinkedHashMap(Java)

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。

它应该支持以下操作: 获取数据 get 和 写入数据 put 。

  • 获取数据 get(key) 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
  • 写入数据 put(key, value) 如果密钥已经存在,则变更其数据值; 如果密钥不存在,则插入该组「密钥/数据值」。
  • 当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

解题思路 : 双向链表 + 哈希表

LRU 缓存机制可以通过哈希表辅以双向链表实现, 我们用一个哈希表和一个双向链表维护所有在缓存中的键值对。

双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的键值对是最久未使用的。

哈希表即为普通的哈希映射(HashMap),通过缓存数据的键映射到其在双向链表中的位置。

实现cpp

#include <iostream>
#include <stack> // 栈使用
#include <unordered_map>
#include <vector> // 向量容器
#include <array>
#include <cctype>
#include <string>

using namespace std;

// 双向链表
struct DLinkedNode {
    int key, value;
    DLinkedNode *prev;
    DLinkedNode *next;
    // 使用初始化列表来初始化字段
    DLinkedNode(): key(0), value(0), prev(nullptr), next(nullptr) {}
    DLinkedNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {}
};

class LRUCache {
private:
    unordered_map<int, DLinkedNode*> cache; // hash表存储
    DLinkedNode *head;
    DLinkedNode *tail;
    int size;
    int capacity;
public:
    LRUCache(int _capacity): capacity(_capacity), size(0) {
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head->next = tail; // 双向链表头尾连接
        tail->prev = head;
    }

    int get(int key) {
        if (!cache.count(key)) {
            return -1;
        }
        DLinkedNode* node = cache[key]; // 拿出节点地址
        moveToHead(node);
        return node->value; // 移动到头部后返回该节点的值
    }

    void put(int key, int value) {
        if (!cache.count(key)) {
            // 不存在。添加一个新节点
            DLinkedNode* node = new DLinkedNode(key, value);
            cache[key] = node;
            addToHead(node);
            ++size;
            if (size > capacity) {
                // size过大移除尾部
                DLinkedNode* remove = removeTail();
                cache.erase(remove->key);
                --size;
            }
        } else {
            // 存在移动到头部
            DLinkedNode* node = cache[key];
            node->value = value;
            moveToHead(node);
        }
    }

    // 定义链表的一些操作:主要是这个链表的操作,添加头部和移除节点

    // 将node节点替换head节点
    void addToHead(DLinkedNode* node) {
        node->prev = head; // 当前节点的前一个设置为head
        node->next = head->next; // 将head的下一个节点交给node
        head->next->prev = node; // head的下一个节点的前一个节节点变为node
        head->next = node; // 头结点的下一个为node
    }

    // 移除节点:通过修改前后节点的指向移除该节点
    void removeNode(DLinkedNode* node) {
        node->prev->next = node->next; // node前一个节点的next节点 设置为node的next
        node->next->prev = node->prev; // node后一个节点的前节点,设置为node的prev
    }

    // 移动到头部
    void moveToHead(DLinkedNode* node) {
        removeNode(node);
        addToHead(node);
    }

    // 移除尾部节点
    DLinkedNode* removeTail() {
        DLinkedNode* node = tail->prev; // 获取虚尾部的前一个节点
        removeNode(node);
        return node;
    }
};

int main() {
    return 0;
}