LRU Cache 双向链表以及STL list实现----面试常考

双向链表版本:
cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
struct Node{
    int key, value;
    Node* prev;
    Node* next;
    Node():key(0), value(0), prev(nullptr), next(nullptr){}
    Node(int k, int v):key(k), value(v), prev(nullptr), next(nullptr){}
};
class LRUCache{
private:
    int capacity_;
    int size_;
    Node* head;
    Node* tail;
    unordered_map<int, Node*> cache_;
public:
    LRUCache(int capacity): capacity_(capacity), size_(0){
        head = new Node();
        tail = new Node();
        head->next = tail;
        tail->prev = head;
    }
    void removeNode(Node* node){
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }
    void addToHead(Node* node){
        node->next = head->next;
        node->prev = head;
        head->next->prev = node;
        head->next = node;
    }
    void moveToHead(Node* node){
        removeNode(node);
        addToHead(node);
    }
    Node* removeTail(){
        auto node = tail->prev;
        removeNode(node);
        return node;
    }
    int get(int key){
        if(cache_.find(key) != cache_.end()){
            moveToHead(cache_[key]);
            return cache_[key]->value;
        }
        return -1;
    }
    void put(int key, int value){
        if(cache_.find(key) != cache_.end()){
            moveToHead(cache_[key]);
            cache_[key]->value = value;
        }
        else{
            if(size_ == capacity_){
                auto node = removeTail();
                cache_.erase(node->key);
                delete node;
                --size_;
            }
            auto node = new Node(key, value);
            addToHead(node);
            cache_.insert({key, node});
            ++size_;
        }
    }
    
    ~LRUCache(){
        while(head){
            auto node = head;
            head = head->next;
            delete node;
        }
    }
    void print(){
        cout << "=================" << endl;
        auto node = head->next;
        while(node != tail){
            cout << "[" << node->key << " " << node->value << "]" << endl;
            node = node->next;
        }
    }
};
int main(){
    auto lru = new LRUCache(2);
    lru->put(1, 1);
    lru->put(2, 2);
    lru->print();
    lru->put(3, 3);
    lru->print();
    cout << lru->get(2) << endl;
    lru->print();
    lru->put(4, 4);
    lru->print();
    cout << lru->get(1) << endl;
    lru->print();
    cout << lru->get(3) << endl;
    return 0;
}

手写双向链表版本比较简单,在纸上画一画就能想通!

STL的list版本:
cpp 复制代码
using pi = pair<int, int>;
class LRUCache {
public:
    LRUCache(int capacity):capacity_(capacity)
    {}
    
    /**
     * 根据给定的键获取缓存中的值。
     * 如果键存在于缓存中,则将该键值对移动到缓存的前端,并返回对应的值。
     * 如果键不存在于缓存中,则返回-1。
     * 
     * @param key 要查询的键。
     * @return 存储在缓存中的键对应的值,如果键不存在则返回-1。
     */
    int get(int key) {
        // 检查键是否存在于缓存中
        if(key_table_.count(key)){
            // 获取键对应的节点
            auto node = key_table_[key];
            // 从节点中提取值
            int value = node->second;
            // 从缓存中删除节点,因为它即将被移动到前端
            cache_.erase(node);
            // 将键值对移动到缓存的前端
            cache_.push_front({key, value});
            // 更新键在映射表中的指针,指向移动到前端的新节点
            key_table_[key] = cache_.begin();
            // 返回键对应的值
            return value;
        }
        // 如果键不存在,返回-1
        return -1;
    }

    /**
     * 将键值对(key, value)放入缓存。
     * 如果键已经存在,则更新对应的值,并从缓存中移除该键值对,以便重新插入以维护LRU顺序。
     * 如果缓存已满,则移除最不常访问的键值对,为新键值对腾出空间。
     * 
     * @param key 要放入缓存的键。
     * @param value 要放入缓存的值。
     */
    void put(int key, int value) {
        // 如果键已经存在,则获取对应的节点
        if(key_table_.count(key)){
            auto node = key_table_[key];
            int value = node->second;
            // 从缓存中移除该节点,因为待会要重新插入以维护LRU顺序
            cache_.erase(node);
        }
        else{
            // 如果缓存已满,则移除最不常访问的键值对
            if(cache_.size() == capacity_){
                auto kv = cache_.back();
                int k = kv.first, v = kv.second;
                cache_.pop_back();
                key_table_.erase(k);
            }
        }
        // 将新的键值对插入到缓存的前端,并更新键映射表
        cache_.push_front({key, value});
        key_table_[key] = cache_.begin();
    }
private:
    unordered_map<int, list<pi>::iterator> key_table_; 
    list<pi> cache_;   // 缓存
    int capacity_;   
};

主要用了迭代器,对于初学者可能难以理解。可以看我的下一篇博客,详细阐述了迭代器设计思想!

相关推荐
智者知已应修善业8 分钟前
【51单片机数码管+蜂鸣器的使用】2023-6-14
c++·经验分享·笔记·算法·51单片机
艾莉丝努力练剑11 分钟前
【Linux线程】Linux系统多线程(七):<线程同步与互斥>线程同步(下)
java·linux·运维·服务器·c++·学习·操作系统
c++逐梦人19 分钟前
C++ RAII流式日志库实现
开发语言·c++
t***54419 分钟前
还有哪些设计模式适合现代C++
开发语言·c++·设计模式
Wave84520 分钟前
C++ 面向对象基础:类、访问权限,构造函数,析构函数
开发语言·c++
t***54421 分钟前
如何在现代C++项目中有效应用这些设计模式
开发语言·c++·设计模式
kyle~23 分钟前
导航---LIO(激光雷达-惯性里程计)算法
c++·算法·机器人·ros2·导航
杰众物联28 分钟前
超高频FPC标签企业
c++
用户8055336980330 分钟前
现代Qt开发教程(新手篇)1.6——内存管理
c++·qt
无限进步_38 分钟前
【C++】私有虚函数与多态:访问权限不影响动态绑定
开发语言·c++·ide·windows·git·算法·visual studio