双语面试:实现一个 LRUCache 类(LeetCode: Med.)

4 阅读2分钟

面试题 Interview Question

实现一个 LRUCache 类,支持 get(key)put(key, value) 方法,均在 O(1) 时间复杂度内完成,并在缓存满时淘汰最近最少使用的项。 力扣

Implement an LRUCache class with get(key) and put(key, value) methods, each operating in O(1) time, and evicting the least-recently-used entry when capacity is exceeded. LeetCode

要求 Requirements

  1. LRUCache(capacity) 构造函数接收一个正整数 capacity,表示缓存的最大容量
    The constructor LRUCache(capacity) takes a positive integer capacity indicating the cache’s maximum size.
  2. get(key)
    • 如果 key 存在,返回对应的 value 并将该项标记为最近使用
      If key exists, return its value and mark it as most recently used
    • 如果不存在,返回 -1
      If not found, return -1.
  3. put(key, value)
    • 如果 key 已存在,更新其 value 并标记为最近使用
      If key exists, update its value and mark it as most recently used
    • 如果 key 不存在且缓存已满,则移除最久未使用的项,再插入新 key-value
      If key is absent and cache is full, remove the least-recently-used item before inserting the new pair.
  4. 所有操作应具有 O(1) 时间复杂度
    All operations must run in O(1) time, requiring a doubly linked list for order tracking and a hash map for fast lookup.

参考答案 Reference Solution

class LRUCache {
  constructor(capacity) {
    this.capacity = capacity;
    this.cache = new Map();         // Hash map for O(1) lookups
  }

  get(key) {
    if (!this.cache.has(key)) {
      return -1;
    }
    const value = this.cache.get(key);
    this.cache.delete(key);         // Remove and re-insert to update recency
    this.cache.set(key, value);
    return value;
  }

  put(key, value) {
    if (this.cache.has(key)) {
      this.cache.delete(key);
    }
    this.cache.set(key, value);     // Insert as most recently used
    if (this.cache.size > this.capacity) {
      // Evict least recently used key (first item in Map)
      const lruKey = this.cache.keys().next().value;
      this.cache.delete(lruKey);
    }
  }
}
  • 以上方案利用现代 JavaScript 中 Map 保持插入顺序的特性来模拟双向链表,无需手写节点结构,但依然满足 O(1) 操作
    This approach leverages the insertion-order guarantee of JavaScript’s modern Map to emulate a doubly linked list without manually implementing node structures, while still achieving O(1) operations. Reddit GeeksforGeeks
  • 对于更底层的面试要求,可使用显式的双向链表+普通对象/Map 组合来展示链表节点移动的思路
    For more low-level interview requirements, you can use an explicit doubly linked list combined with plain objects or a Map to demonstrate the mechanics of moving list nodes.

示例 Example

const cache = new LRUCache(2);

cache.put(1, 'A');     // 缓存: {1=A}
cache.put(2, 'B');     // 缓存: {1=A, 2=B}
cache.get(1);          // 返回 'A', 更新顺序: {2=B, 1=A}
cache.put(3, 'C');     // 容量已满, 淘汰 LRU 键 2 -> 缓存: {1=A, 3=C}
cache.get(2);          // 返回 -1 (未找到)
cache.put(4, 'D');     // 淘汰键 1 -> 缓存: {3=C, 4=D}
cache.get(1);          // 返回 -1
cache.get(3);          // 返回 'C'
cache.get(4);          // 返回 'D'

面试考察点 Interview Focus

  • 时间/空间复杂度:理解如何在 O(1) 时间内完成缓存查找、更新与淘汰。
    Time/space complexity: how to achieve O(1) for get/put/evict.
  • 数据结构设计:双向链表+哈希表组合或利用 Map 保序机制的巧思。
    Data-structure design: doubly linked list + hash map, or leveraging Map’s insertion order.
  • 代码可靠性:边界条件(如容量为 0、重复 putget 不存在的键)处理。
    Robustness: handling edge cases (capacity 0, duplicate puts, missing gets).