友情支持

如果您觉得这个笔记对您有所帮助,看在D瓜哥码这么多字的辛苦上,请友情支持一下,D瓜哥感激不尽,😜

支付宝

微信

有些打赏的朋友希望可以加个好友,欢迎关注D 瓜哥的微信公众号,这样就可以通过公众号的回复直接给我发信息。

wx jikerizhi

公众号的微信号是: jikerizhi因为众所周知的原因,有时图片加载不出来。 如果图片加载不出来可以直接通过搜索微信号来查找我的公众号。

460. LFU 缓存

请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。

实现 LFUCache 类:

  • LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象

  • int get(int key) - 如果键 key 存在于缓存中,则获取键的值,否则返回 -1

  • void put(int key, int value) - 如果键 key 已存在,则变更其值;如果键不存在,请插入键值对。当缓存达到其容量 capacity 时,则应该在插入新项之前,移除最不经常使用的项。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最久未使用 的键。

为了确定最不常使用的键,可以为缓存中的每个键维护一个 使用计数器。使用计数最小的键是最久未使用的键。

当一个键首次插入到缓存中时,它的使用计数器被设置为 1 (由于 put 操作)。对缓存中的键执行 getput 操作,使用计数器的值将会递增。

函数 getput 必须以 O(1) 的平均时间复杂度运行。

示例:

输入:
["LFUCache", "put", "put", "get", "put", "get", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [3], [4, 4], [1], [3], [4]]
输出:
[null, null, null, 1, null, -1, 3, null, -1, 3, 4]

解释:
// cnt(x) = 键 x 的使用计数
// cache=[] 将显示最后一次使用的顺序(最左边的元素是最近的)
LFUCache lfu = new LFUCache(2);
lfu.put(1, 1);   // cache=[1,_], cnt(1)=1
lfu.put(2, 2);   // cache=[2,1], cnt(2)=1, cnt(1)=1
lfu.get(1);      // 返回 1
                 // cache=[1,2], cnt(2)=1, cnt(1)=2
lfu.put(3, 3);   // 去除键 2 ,因为 cnt(2)=1 ,使用计数最小
                 // cache=[3,1], cnt(3)=1, cnt(1)=2
lfu.get(2);      // 返回 -1(未找到)
lfu.get(3);      // 返回 3
                 // cache=[3,1], cnt(3)=2, cnt(1)=2
lfu.put(4, 4);   // 去除键 1 ,1 和 3 的 cnt 相同,但 1 最久未使用
                 // cache=[4,3], cnt(4)=1, cnt(3)=2
lfu.get(1);      // 返回 -1(未找到)
lfu.get(3);      // 返回 3
                 // cache=[3,4], cnt(4)=1, cnt(3)=3
lfu.get(4);      // 返回 4
                 // cache=[3,4], cnt(4)=2, cnt(3)=3

提示:

  • 1 <= capacity <= 104

  • 0 <= key <= 105

  • 0 <= value <= 109

  • 最多调用 2 * 105getput 方法

思路分析

使用两个 Map,一个存数据,另外一个存频率到头节点的关联关系。代码还没调试通!明天继续!

  • 一刷

  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
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/**
 * @author D瓜哥 · https://www.diguage.com
 * @since 2025-08-26 22:05:19
 */
static class LFUCache {
  private int capacity;
  private Map<Integer, Node> keyToData;
  private final Node HEAD;
  private final Node TAIL;
  private Map<Integer, Node> countToNode;

  public LFUCache(int capacity) {
    this.capacity = capacity;
    this.keyToData = new HashMap<>();
    countToNode = new HashMap<>();
    HEAD = new Node(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
    TAIL = new Node(Integer.MIN_VALUE, Integer.MIN_VALUE, 1);
    HEAD.next = TAIL;
    TAIL.prev = HEAD;
    countToNode.put(1, TAIL);
  }

  public int get(int key) {
    Node node = keyToData.get(key);
    if (node == null) {
      return -1;
    }
    updateList(node);
    return node.value;
  }

  public void put(int key, int value) {
    Node node = keyToData.get(key);
    if (node == null) {
      if (keyToData.size() == capacity) {
        deleteNode(TAIL.prev);
      }
      node = new Node(key, value);
    }
    node.value = value;
    updateList(node);
  }

  private void updateList(Node node) {
    Node prev = getPrevNode(node);
    if (node.next != null) {
      Node next = node.next;
      if (next.count == node.count) {
        countToNode.put(next.count, next);
      } else {
        countToNode.remove(node.count);
      }
    } else {
      countToNode.remove(node.count);
    }
    putAfter(prev, node);
  }

  private Node getPrevNode(Node node) {
    Node next = countToNode.get(node.count + 1);
    if (next == null) {
      next = countToNode.get(node.count);
    }
    return next.prev;
  }

  private void deleteNode(Node node) {
    node.prev.next = node.next;
    node.next.prev = node.prev;
    keyToData.remove(node.key);
    if (node.next != null) {
      if (node.next.count == node.count) {
        countToNode.put(node.next.count, node.next);
      } else {
        countToNode.remove(node.count);
      }
    }
  }

  private void putAfter(Node prev, Node node) {
    node.count++;
    node.time = System.currentTimeMillis();
    if (node.prev != null) {
      node.prev.next = node.next;
    }
    if (node.next != null) {
      node.next.prev = node.prev;
    }
    node.prev = prev;
    Node next = prev.next;
    node.next = next;
    next.prev = node;
    prev.next = node;
    keyToData.put(node.key, node);

    countToNode.put(node.count, node);
  }

  private static class Node {
    int key;
    int value;
    long time;
    int count;
    Node prev;
    Node next;

    public Node(int key, int value) {
      this(key, value, 0);
    }

    public Node(int key, int value, int count) {
      this.key = key;
      this.value = value;
      this.count = count;
      this.time = System.currentTimeMillis();
    }
  }
}

参考资料