Featured image of post LRUCache

LRUCache

31. LRUCache

分析

LRU(Least Recently Used)是一种缓存替换策略,淘汰最久未被使用的数据,以保证缓存内存的有效利用率

  1. 核心数据结构:

    • 双向链表:维护缓存访问顺序,最新使用的数据放在链表头,最久未使用的数据放在链表尾。双向链表的插入和删除操作可以在 O(1) 时间内完成,适用于缓存频繁更新的场景
    • 哈希表:通过关键字快速定位链表中的节点
  2. get(key)

    • key 存在,将对应节点移动到链表头,并返回其值
    • key 不存在,返回 -1
  3. put(key, value)

    • key 已存在,更新其值并移动到链表头
    • key 不存在:
    • 如果缓存已满,删除链表尾的节点(最久未使用)
    • 将新节点插入到链表头

时间复杂度

  • get(key)O(1)

    • 哈希表查找节点是 O(1) ,双向链表操作也是 O(1)
  • put(key, value)O(1)

    • 哈希表插入和删除是 O(1) ,双向链表操作也是 O(1)

空间复杂度

  • 哈希表和双向链表的空间复杂度均为 O(n) ,其中 n 是缓存容量

C++代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
class LRUCache {
public:
    // 定义双向链表节点
    struct Node
    {
      Node(int key, int value)
        : m_key(key), m_value(value)
        , prev(nullptr), next(nullptr)
      {}

      int m_key, m_value;
      Node *prev, *next;
    };

    int n;                                 // 缓存容量
    Node *head, *tail;                     // 双链表的虚拟头节点
    std::unordered_map<int, Node*> hash;   // 哈希表,快速查找节点

    // 初始化 LRU 缓存
    LRUCache(int capacity)
    {
      n = capacity;
      head = new Node(-1, -1);             // 创建虚拟头节点
      tail = new Node(-1, -1);             // 创建虚拟尾节点
      head->next = tail;                   // 初始化双向链表
      tail->prev = head;
    }

    // 从链表中删除指定节点
    void remove(Node* cur)
    {
      cur->next->prev = cur->prev;
      cur->prev->next = cur->next;
    }

    // 在链表头部插入指定节点
    void insert(Node* cur)
    {
      cur->next = head->next;
      cur->prev = head;
      head->next->prev = cur;
      head->next = cur;
    }

    // 获取节点的值
    int get(int key)
    {
      if (!hash.count(key))                 // 节点不存在
        return -1;
      Node* cur = hash[key];                // 定位节点
      remove(cur);                          // 更新节点在链表中的位置
      insert(cur);
      return cur->m_value;
    }

    // 插入或更新节点
    void put(int key, int value)
    {
      if (hash.count(key))                  // 节点已存在
      {
        Node* cur = hash[key];
        cur->m_value = value;               // 更新节点值
        remove(cur);                        // 更新节点位置
        insert(cur);
      }
      else                                  // 节点不存在
      {
        if (hash.size() == n)               // 缓存已满
        {
          Node* cur = tail->prev;           // 取出尾节点
          remove(cur);                      // 从链表中移除
          hash.erase(cur->m_key);           // 从哈希表中删除
        }
        Node* cur = new Node(key, value);   // 创建新节点
        insert(cur);                        // 插入链表头
        hash[key] = cur;                    // 添加到哈希表
      }
    }
};
Built with Hugo
Theme Stack designed by Jimmy