运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。
获取数据 get(key) - 如果关键字 (key) 存在于缓存中,则获取关键字的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字/值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。进阶:
你是否可以在 O(1) 时间复杂度内完成这两种操作?
示例:
LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 该操作会使得关键字 2 作废
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 该操作会使得关键字 1 作废
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lru-cache
class LRUCache { //LRU 缓存机制可以通过哈希表辅以双向链表实现,我们用一个哈希表和一个双向链表维护所有在缓存中的键值对。 private static class Node{ private int key; private int value; private Node prev; //前置结点 private Node next; //下一个节点 public Node(){} public Node(int key,int value){ this.key = key; this.value = value; } } //当容量满的时候,采用LRU淘汰策略 //1.淘汰最少使用的数据 //2.淘汰最早放进来的数据 private Map<Integer,Node> map = new HashMap<Integer,Node>(); //标识容量 是一个固定值 private int capacity; //标识大小,当前大小 private int size; //头结点 private Node head; //尾节点 private Node tail; public LRUCache(int capacity) { this.capacity = capacity; this.size = 0; // 使用伪头部和伪尾部节点 head = new Node(); tail = new Node(); head.next = tail; tail.prev = head; } //如果 key 不存在,则返回 -1−1; //如果 key 存在,则 key 对应的节点是最近被使用的节点。通过哈希表定位到该节点在双向链表中的位置,并将其移动到双向链表的头部,最后返回该节点的值 public int get(int key) { Node result = map.get(key); if(result == null){ return -1; } //通过哈希表定位到该节点在双向链表中的位置,并将其移动到双向链表的头部 result.prev.next = result.next; result.next.prev = result.prev; //把当前节点放到头结点 result.prev = head; result.next = head.next; head.next.prev = result; head.next = result; return result.value; } /** 对于 put 操作,首先判断 key 是否存在: 如果 key 不存在,使用 key 和 value 创建一个新的节点,在双向链表的头部添加该节点,并将 key 和该节点添加进哈希表中。然后判断双向链表的节点数是否超出容量,如果超出容量,则删除双向链表的尾部节点,并删除哈希表中对应的项; 如果 key 存在,则与 get 操作类似,先通过哈希表定位,再将对应的节点的值更新为 value,并将该节点移到双向链表的头部。 **/ public void put(int key, int value) { Node result = map.get(key); //若是没有则放入新的node节点 if(result == null){ Node node = new Node(key,value); //添加到hash表 map.put(key,node); //把新加入的节点放到链表的头部 node.prev = head; node.next = head.next; head.next.prev = node; head.next = node; ++size; //如果当前大小已经超出了容量,就把最久未使用的删除 if(size > capacity){ //去除hash表中的值 map.remove(tail.prev.key); //去除链表中最久未使用的节点 tail.prev.prev.next = tail; tail.prev = tail.prev.prev; --size; } } else { //若是有值,需要把原来的值覆盖 result.value = value; map.put(key,result); //把链表中的节点放到头结点 //先把当前节点删除 result.prev.next = result.next; result.next.prev = result.prev; //把当前节点放到头结点 result.prev = head; result.next = head.next; head.next.prev = result; head.next = result; } } } /** * Your LRUCache object will be instantiated and called as such: * LRUCache obj = new LRUCache(capacity); * int param_1 = obj.get(key); * obj.put(key,value); */