LeetCode 146. LRU 缓存机制
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:
LRUCache(int capacity)以正整数作为容量capacity初始化 LRU 缓存int get(int key)如果关键字key存在于缓存中,则返回关键字的值,否则返回 -1 。void put(int key, int value)如果关键字已经存在,则变更其数据值;如果关键字不存在,则 插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
进阶: 你是否可以在 O(1) 时间复杂度内完成这两种操作?
分析
LRUCache 类实现后需要满足的条件:
- O(1) 时间复杂度完成 get & put 操作
- get 完成时,需要将 get 的数据设置为最新
- put 数据时,当超出容量时,需要将最久未使用的数据删除,并将 put 的数据设置为最新
由上可得:
- 通过链表来标识出数据的新旧关系,新的数据放到后面即可
- 需要 O(1) 的时间复杂度完成操作,则还需要使用哈希表来存储数据
- 设置数据为最新则可以通过先删除数据,然后再添加数据到末尾来实现
- 为了实现快速删除数据,我们需要使用双向链表
具体实现
存储数据的节点 Node:
class Node {
public int key, val;
public Node prev, next;
public Node(int k, int v){
this.key = k;
this.val = v;
}
}
用户实现快速操作的 Map:
HashMap<Integer, Node> map;
用于实现 LRU 的双向链表:
class DoubleList {
private Node head, tail;
private int size;
public DoubleList() {
}
public void addLast(Node node) {
}
public void remove(Node node) {
}
public Node removeFirst() {
}
public int size() {
}
}
LRUCache 具体实现:
import java.util.HashMap;
public class LRUCache {
private int cap;
private HashMap<Integer, Node> map;
public DoubleList cache;
public LRUCache(int capacity) {
this.cap = capacity;
map = new HashMap();
cache = new DoubleList();
}
class Node {
public int key, val;
public Node prev, next;
public Node(int k, int v){
this.key = k;
this.val = v;
}
}
class DoubleList {
private Node head, tail;
private int size;
public DoubleList() {
head = new Node(-1, -1);
tail = new Node(-1, -1);
head.next = tail;
tail.prev = head;
size = 0;
}
public void addLast(Node node) {
tail.prev.next = node;
node.prev = tail.prev;
node.next = tail;
tail.prev = node;
size++;
}
public void remove(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
map.remove(node.key);
size--;
}
public Node removeFirst() {
if (head.next == tail) {
return null;
}
Node first = head.next;
remove(first);
return first;
}
public int size() {
return this.size;
}
}
public Integer get(int key) {
if (!map.containsKey(key)) {
return -1;
}
Node node = map.get(key);
cache.remove(node);
cache.addLast(node);
return map.get(key).val;
}
public void put(int key, int val) {
Node node = new Node(key, val);
if (map.containsKey(key)) {
map.put(key, node);
cache.remove(node);
} else {
if (this.cap > cache.size()) {
map.put(key, node);
} else {
cache.removeFirst();
}
}
cache.addLast(node);
}
public static void main(String[] args) {
LRUCache cache = new LRUCache(3);
cache.put(0,0);
cache.put(1,1);
cache.put(2,2);
cache.put(3,3);
System.out.println(cache.get(0));
Node p = cache.cache.head;
System.out.println("------cache-------");
while (p!= null) {
System.out.println(p.val);
p = p.next;
}
}
}
使用 LinkedHashMap 实现
Java 集合提供 LinkedHashMap 的底层和上述实现类似,可以直接被用来实现 LRU:
import java.util.LinkedHashMap;
public class LRUCache1 {
private int capacity;
private LinkedHashMap<Integer, Integer> cache;
public LRUCache1(int capacity) {
this.capacity = capacity;
cache = new LinkedHashMap<>();
}
public Integer get(int key) {
if (!cache.containsKey(key)) {
return -1;
} else {
Integer val = cache.get(key);
cache.remove(key);
cache.put(key, val);
return val;
}
}
public void put(int key, int val) {
if (cache.containsKey(key)) {
cache.remove(key);
}
if (cache.size() >= capacity) {
Integer first = cache.keySet().iterator().next();
cache.remove(first);
}
cache.put(key, val);
}
public static void main(String[] args) {
LRUCache1 cache = new LRUCache1(2);
cache.put(1,1);
cache.put(2,2);
cache.put(3,3);
System.out.println(cache.get(1));
}
}