146. LRU 缓存

【题目】:146. LRU 缓存

LRU:最近最少未使用,很少被请求的数据才会被淘汰掉

本质:让不经常访问的数据往下排,经常访问的数据往上排。

这样会导致:冷门数据在最下边,热门数据在最上边。

如果访问的数据缓存中没有缓存已经满了:把最下边的数据淘汰掉,再把刚访问的数据放到上边(换页)

分析:

  1. 选用什么数据结构?
    数组:添加一个节点,所有节点都要往后移动,时间复杂度O(n)
    链表:添加节点、删除节点只需要改变节点指向,时间复杂度O(1)。只需要改变指针的指向即可。由于不仅要直到节点的下一位,还需要直到节点的上一位,所以使用双向链表
  2. 怎么找到链表的某个节点?
    正常查询链表的某个节点,需要逐个遍历链表元素,时间复杂度O(n)。那有没有办法优化到O(1)的时间复杂度呢?

hashMap的查询时间复杂度是O(1),可以用hashMap来存储链表节点。

最后选择数据结构如下:

  1. 双向链表数据结构
cpp 复制代码
class Node {
public:    
    int key;
    int value;
    Node *pre;
    Node *next;
    Node(int key, int value) {
        this->key = key;
        this->value = value;
        this->pre = nullptr;
        this->next = nullptr;
    }
    Node() {
        this->key = 0;
        this->value = 0;
        this->pre = nullptr;
        this->next = nullptr;
    }
};
cpp 复制代码
class LRUCache {
public:
    int capacity;
    int size;

    Node *head; // 头节点(哨兵)
    Node *tail; // 尾节点(哨兵)

    unordered_map<int, Node*> mp; // map

    LRUCache(int capacity) {
        this->capacity = capacity;
        this->size = 0;
        // 初始化双向链表
        head = new Node();
        tail = new Node();
        head->next = tail;
        tail->pre = head;
    }
    
    int get(int key) {
        if(mp.find(key) == mp.end()) { // 未找到
            return -1;
        }
        Node *node = mp[key];
        deleteNodeAndInsertHead(node);
        return node->value;
    }
    
    void put(int key, int value) {
        if(mp.find(key) == mp.end()) { // 缓存中没有
            if(size == capacity) { // 缓存已满,调页
                Node *delNode = tail->pre; // 要调出的节点
                deleteNode(delNode); // 调出
                mp.erase(delNode->key);// 更新map
            }else {
                // 缓存未满 - 直接提到头部
                ++size; //更新size
            }
            Node *node = new Node(key, value);
            insertHead(node); // 提到头部
            mp[key] = node; // 更新map
        }else {
            // 缓存中有 - 更新 - 提到头部
            Node *node = mp[key];
            node->value = value; // 更新node
            mp[key] = node; // 更新map
            deleteNodeAndInsertHead(node); // 提到头部
        }
    }
private:
    // 删除节点
    void deleteNode(Node *node) {
        node->pre->next = node->next;
        node->next->pre = node->pre;
    }
    // 插入头部
    void insertHead(Node *node) {
        head->next->pre = node;
        node->next = head->next;
        node->pre = head;
        head->next = node;
    }
    // 删除节点插入头部的连续操作(正常访问一个节点,都需要做这两步操作)
    void deleteNodeAndInsertHead(Node *node) {
        deleteNode(node);
        insertHead(node);
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)
相关推荐
gfdhy1 天前
【c++】哈希算法深度解析:实现、核心作用与工业级应用
c语言·开发语言·c++·算法·密码学·哈希算法·哈希
百***06011 天前
SpringMVC 请求参数接收
前端·javascript·算法
一个不知名程序员www1 天前
算法学习入门---vector(C++)
c++·算法
云飞云共享云桌面1 天前
无需配置传统电脑——智能装备工厂10个SolidWorks共享一台工作站
运维·服务器·前端·网络·算法·电脑
福尔摩斯张1 天前
《C 语言指针从入门到精通:全面笔记 + 实战习题深度解析》(超详细)
linux·运维·服务器·c语言·开发语言·c++·算法
橘颂TA1 天前
【剑斩OFFER】算法的暴力美学——两整数之和
算法·leetcode·职场和发展
xxxxxxllllllshi1 天前
【LeetCode Hot100----14-贪心算法(01-05),包含多种方法,详细思路与代码,让你一篇文章看懂所有!】
java·数据结构·算法·leetcode·贪心算法
前端小L1 天前
图论专题(二十二):并查集的“逻辑审判”——判断「等式方程的可满足性」
算法·矩阵·深度优先·图论·宽度优先
铁手飞鹰1 天前
二叉树(C语言,手撕)
c语言·数据结构·算法·二叉树·深度优先·广度优先
专业抄代码选手1 天前
【Leetcode】1930. 长度为 3 的不同回文子序列
javascript·算法·面试