运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。
获取数据 get(key) - 如果关键字 (key) 存在于缓存中,则获取关键字的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字/值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
你是否可以在 O(1) 时间复杂度内完成这两种操作?
代码:
public class LRUCache {
private Map<Integer, Node> cache = new HashMap<Integer, Node>();
private DoubleLinkedList list = new DoubleLinkedList();
private int capacity;
public LRUCache(int capacity) {
this.capacity = capacity;
}
public int get(int key) {
Node node = cache.get(key);
if (node == null) {
return -1;
}
// 如果 key 存在,先通过哈希表定位,再移到头部
list.moveToHead(node);
return node.value;
}
public void put(int key, int value) {
Node node = cache.get(key);
if (node == null) {
// 如果 key 不存在,创建一个新的节点
Node newNode = new Node(key, value);
// 添加进哈希表
cache.put(key, newNode);
// 添加至双向链表的头部
list.addToHead(newNode);
if (list.size > capacity) {
// 如果超出容量,删除双向链表的尾部节点
Node tail = list.removeTail();
// 删除哈希表中对应的项
cache.remove(tail.key);
}
}
else {
// 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
node.value = value;
list.moveToHead(node);
}
}
}
class Node{
int key;
int value;
Node prev;
Node next;
public Node(){}
public Node(int key,int value){
this.key = key;
this.value = value;
}
}
//实现双向链表
class DoubleLinkedList{
private Node head;
private Node tail;
public int size;
public DoubleLinkedList(){
this.head = new Node();
this.tail = new Node();
this.size = 0;
head.next = tail;
tail.prev = head;
}
public void addToHead(Node node){
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
this.size += 1;
}
public void removeNode(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
this.size -= 1;
}
public void moveToHead(Node node) {
removeNode(node);
addToHead(node);
}
public Node removeTail() {
Node res = tail.prev;
removeNode(res);
return res;
}
}