题目
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
- LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
- int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
- void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
解析
通过一个双向链表+map实现LRU,双向链表设置一个哨兵节点,用于快速插入和快速删除。
put方法:
- 如果已经在map,就直接改变值,然后将节点放到头部
- 如果不存在
- 放到map
- 节点放到前面
- 判断数量,如果超过容量,就去掉尾部节点
get方法:

代码
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
|
class LRUCache {
private class ListNode{ int key,value; ListNode pre,next;
public ListNode() { }
public ListNode(int key, int value) { this.key = key; this.value = value; } }
private int count; private Map<Integer, ListNode> map; private ListNode head; private int capacity;
public LRUCache(int capacity) { count=0; map=new HashMap<>(); head=new ListNode(); head.pre=head; head.next=head; this.capacity=capacity; }
public int get(int key) { if(!map.containsKey(key)) return -1; ListNode listNode = map.get(key); removeNode(listNode); addNode(listNode); return listNode.value; }
public void put(int key, int value) { if(map.containsKey(key)){ ListNode listNode = map.get(key); listNode.value=value; removeNode(listNode); addNode(listNode); return ; }
ListNode node=new ListNode(key,value); addNode(node); map.put(key,node); count++;
if(count>=capacity){ map.remove(head.pre.key); removeNode(head.pre); count--; } }
private void removeNode(ListNode node){ node.pre.next=node.next; node.next.pre=node.pre; }
private void addNode(ListNode node){ node.next=head.next; node.pre=head; node.next.pre=node; node.pre.next=node; }
}
|
总结
本 LRUCache 实现通过 “双向循环链表(含哨兵节点)+ 哈希表” 的组合方案,既利用哈希表实现键到节点的 O (1) 查找,又借助双向链表完成节点 O (1) 插入 / 删除以维护 “最近使用(头部)- 最久未使用(尾部)” 顺序;构造函数初始化缓存容量与基础结构,get 方法通过查找节点、删除后插入头部更新使用顺序并返回值,put 方法分 “键存在则更新值并移头部”“键不存在则新增节点、超容时淘汰尾部节点” 两种场景处理,辅助方法 removeNode 和 addNode 简化节点操作,最终实现 get 与 put 操作 O (1) 平均时间复杂度,同时满足 LRU 缓存的初始化、数据存取及超容淘汰核心需求,哨兵节点设计还避免了边界判断,提升代码简洁性。