设计和构建一个“最近最少使用”缓存,该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值),并在初始化时指定最大容量。当缓存被填满时,它应该删除最近最少使用的项目。
get和put函数的时间复杂度因为O(1)
HashMap和双向链表=LinkedHashMap的链式版本
HashMap:存放缓存数据
双向链表:存放节点顺序
缓存容量:2
依次进行以下操作:
1. put(1,1)
2. put(2,2)
3. get(1)
3. put(3,3)
4. put(4,4)
图1 初始化虚拟头节点和尾节点
图2 插入1
图3 插入2
图4 获取key为1的值
由于要删除最近访问的值,因此将key为1的节点移到头节点
细节:先移除,再移动(这里要注意顺序,不然容易出问题,如果先移动到头节点,再移除会出错,如图5)。
图5 先移动后移除图
图6 put(3,3)
由于此时缓存元素为2,再插入一个元素,超过缓存容量,所以需要移除尾节点。然后将插入的元素移动到头部,如图6所示。
图7 put(4,4)
解释如4所示。
class LRUCache {int size; // 元素个数int capacity; // 容量BiNode head; // 头节点BiNode tail; // 尾节点Map cache; // 缓存public LRUCache(int capacity) {this.size = 0;this.capacity = capacity;this.cache = new HashMap<>();head = new BiNode();tail = new BiNode();head.next = tail;tail.pre = head;}public int get(int key) {BiNode node = cache.get(key);if (node == null) {return -1; // 不存在该元素} else {removeNode(node); // 移除老元素addToHead(node); // 放到头部return node.value;}}public void put(int key, int value) {BiNode node = cache.get(key);if(node == null){ // 不存在该元素BiNode newNode = new BiNode(key, value);addToHead(newNode); // 放到头部cache.put(key, newNode); // 放入缓存size++;if(size > capacity){int K = tail.pre.key; // 移除的keycache.remove(K); // 从缓存移除removeTailNode(); // 移除尾部节点size--;}}else { // 存在该元素BiNode newNode = new BiNode(key, value);addToHead(newNode); // 放到头部removeNode(node); // 移除老节点cache.put(key, newNode); // 放入缓存}}private void removeNode(BiNode node){node.next.pre = node.pre;node.pre.next = node.next;}private void removeTailNode(){BiNode node = tail.pre;removeNode(node);node = null; // help GC}private void addToHead(BiNode node){node.next = head.next;node.next.pre = node;node.pre = head;head.next = node;}class BiNode{BiNode pre; // 前向指针BiNode next; // 后向指针int key; // keyint value; // valuepublic BiNode(){}public BiNode(int K, int V){key = K;value = V;}}}