运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache
类:
LRUCache(int capacity)
以正整数作为容量 capacity
初始化 LRU 缓存
int get(int key)
如果关键字 key
存在于缓存中,则返回关键字的值,否则返回 -1
。
void put(int key, int value)
如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
- 要求在
O(1)
时间复杂度内完成get
和put
操作
实现思路及参考代码
要在O(1)
时间内完成get
和put
操作,很自然的想到哈希表结构,但是很明显页面在置换时对顺序是有要求的。如果使用链表结构,在get
操作上是O(n)
的复杂度。故可以考虑采用哈希表加上双向链表的结构,哈希表存储对应的结点。
原生实现
先定义双向链表节点类
1 2 3 4 5 6 7 8 9
| class Node{ public int key, val; public Node next, prev; public Node(int k, int v) { this.key = k; this.val = v; } }
|
使用Node
构建双向链表
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
| class DoubleList{ private Node head, tail; private int size;
public DoubleList() { this.head = new Node(0,0); this.tail = new Node(0,0); head.next = tail; tail.prev = head; size = 0; }
public void addLast(Node node) { node.prev = tail.prev; node.next = tail; tail.prev.next = node; tail.prev = node; size++; }
public void remove(Node node) { node.prev.next = node.next; node.next.prev = node.prev; size--; }
public Node removeFirst() { if (head.next == tail) return null; Node node = head.next; remove(node); return node; }
public int size(){ return size; } }
|
其中删除操作的时间复杂度是O(1)
,因为是双向链表所以可以直接找到前驱节点,如果是单链表删除操作会需要O(n)
时间复杂度。
双链表结合哈希表实现LRUCache
类
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
| class LRUCache{ private HashMap<Integer, Node> map; private DoubleList cache; private int capacity;
public LRUCache(int capacity) { this.map = new HashMap<>(); this.cache = new DoubleList(); this.capacity = capacity; }
public int get(int key) { if (!map.containsKey(key)) return -1; int val = map.get(key).val; makeRecently(key); return val; }
public void put(int key, int value) { if (map.containsKey(key)) { map.put(key, value); makeRecently(key); return ; } if (cache.size() == this.capacity) { Node oldestNode = cache.removeFirst(); map.remove(oldestNode.key); }
Node newNode = new Node(key, value); map.put(key, newNode); cache.addLast(newNode); }
public void makeRecently(int key) { Node newestNode = map.get(key); cache.remove(newestNode); cache.addLast(newestNode); } }
|
使用LinkedHashMap
Java 内置集合类型LinkedHashMap
是一个哈希链表。
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
| class LRUCache{ LinkedHashMap<Integer, Integer> cache; int capacity;
public LRUCache(int capacity) { cache = new LinkedHashMap<>(); this.capacity = capacity; }
public int get(int key) { if (!cache.containsKey(key)) return -1; makeRecently(key); return cache.get(key); }
public void put(int key, int value) { if (cache.containsKey(key)) { cache.put(key, value); makeRecently(key); return ; } if (this.capacity == cache.size()) { int oldestNode = cache.keySet().iterator().next(); cache.remove(oldestNode); } cache.put(key, value); makeRecently(key); }
public void makeRecently(int key) { int val = cache.get(key); cache.remove(key); cache.put(key, val); } }
|
最简实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class LRUCache extends LinkedHashMap<Integer, Integer>{ private int capacity;
public LRUCache(int capacity) { super(capacity, 0.75F, true); this.capacity = capacity; }
public int get(int key) { return super.getOrDefault(key, -1); }
public void put(int key, int value) { super.put(key, value); }
@Override protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) { return size() > capacity; } }
|
本文参考:labuladong 的算法小抄