题目
运用所掌握的数据结构,设计和实现一个 LRU (Least Recently Used,最近最少使用) 缓存机制 。
实现 LRUCache 类:
LRUCache(int capacity)以正整数作为容量 capacity 初始化 LRU 缓存int get(int key)- 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value)- 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
- 输入与输出

思路
- 双向链表 + 哈希表
- 想到存储的方式是键值对,那么离 哈希表 就不远了;
- 但想一想,有个 缓存记录!也就是说,给定一个固定的空间,如果空间满了,删除最不常用的。这个最不常用的判断就是 插入得早,没被 get 过;
- 而 哈希表 的顺序并不能用代码控制,因此需要一个辅助的数据结构来完成;
- 链表的作用是 记录常用、新增与不常用的键值对,常用、新增放在头部,不常用放在尾部;
- Java 标准库内置 API LinkedHashMap
- 思路都是一样,但是 LinkedHashMap 能够保证插入是有序的;
- LinkedHashMap 新增与不常用的键值对,常用、新增放在尾部(尾部尾部尾部),不常用放在头部
还是看代码,代码中分析的更好一些!!!
代码
- 自己实现双向链表
class LRUCache {
// 存储 key, Node 而不是 key,value
// 方便 get() 方法中 map.get(key).val;
Map<Integer,Node> map;
// 用来判断元素常用或者不常用
DoubleList list;
// 用于判断当前空间是否满了
int size;
// 初始化
public LRUCache(int capacity) {
map = new HashMap<>();
list = new DoubleList();
size = capacity;
}
// 返回 map 中 key - node 键值对 中 node.val
public int get(int key) {
// 不包含返回 -1
if (!map.containsKey(key)) {
return -1;
}
// 更新该元素的常用程度
// 进入 put 方法第一个分支
put(key,map.get(key).val);
return map.get(key).val;
}
// put 要考虑 3 中情况
// 1. 如果包含 key, 则更新 map 中的值与链表中的顺序
// 2. 不包含 key, 但是空间已满, 删除链表最后一个元素(不常用), 执行第 3 步
// 3. 不包含 key, 空间没满, 直接插入 map 和链表中即可
public void put(int key, int value) {
Node node = new Node(key,value);
if (map.containsKey(key)) {
list.remove(map.get(key));
list.addFirst(node);
map.put(key,node);
}else {
if (list.size() == size) {
Node last = list.removeLast();
map.remove(last.key);
}
map.put(key,node);
list.addFirst(node);
}
}
}
// 双向链表类中, 保证了时间复杂度都是 O(1)
class DoubleList {
Node head;
Node tail;
int size;
void addFirst(Node node) {
if (head == null) {
head = node;
tail = node;
}else {
node.next = head;
head.prev = node;
head = node;
}
size++;
}
void remove(Node node) {
if (head == node && tail == node) {
head = null;
tail = null;
}else if (node == tail) {
tail = node.prev;
node.prev = null;
tail.next = null;
}else if (node == head) {
head = head.next;
head.prev = null;
node.next = null;
}else {
node.prev.next = node.next;
node.next.prev = node.prev;
}
size--;
}
Node removeLast() {
Node res = tail;
remove(res);
return res;
}
int size() {
return size;
}
}
// 创建节点
class Node {
int key;
int val;
Node next;
Node prev;
public Node(int key, int val) {
this.key = key;
this.val = val;
}
}
- Java 标准库内置 API
LinkedHashMap
class LRUCache {
// 初始化
LinkedHashMap<Integer,Integer> map;
// 判断空间是否满了
int size;
public LRUCache(int capacity) {
map = new LinkedHashMap<>();
size = capacity;
}
// map 不包含 key, 返回 -1
// map 包含key, 删除重新添加到尾巴节点
public int get(int key) {
if (!map.containsKey(key)) {
return -1;
}
int val = map.remove(key);
map.put(key,val);
return val;
}
// 1. 包含则删除并更新, 放在最后一个节点
// 2. 不包含则添加
// 3. 如果空间满了, 那么删除头结点
public void put(int key, int value) {
if (map.containsKey(key)) {
map.remove(key);
map.put(key,value);
}else {
map.put(key,value);
if (map.size() > size) {
map.remove(map.entrySet().iterator().next().getKey());
}
}
}
}
浙公网安备 33010602011771号