LeetCode 146. LRU缓存机制
题目描述:
运用你所掌握的数据结构,设计和实现一个 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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
LRU(Least Recently Used),是一种缓存淘汰策略,即最久未使用,
LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被
访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问
到的数据淘汰。
其实可以利用java中的现有LinkHashMap(哈希链表)来实现,也有类似淘汰策略的函数
removeEldestEntry(),只需要重写该方法就行;但是我们也可以自己用HashMap+双向链表来实现。
代码
##利用库函数实现
package test;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
public class LRU extends LinkedHashMap<Integer,Integer>{
private int capacity;//容量
public LRU(int capacity) {
super(capacity,0.75f,true);
this.capacity=capacity;
}
public synchronized int get(int key) {
return super.getOrDefault(key, -1);
}
public synchronized void put(int key, int value) {
super.put(key, value);
}
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return size()>capacity;
}
}
###利用hashMap+双向链表实现
package test2;
import java.util.HashMap;
public class LRUCache {
private HashMap<Integer,Node> hashMap;
private DoubleLinkedList linkedList;
private int cacheSize;//容器大小
public LRUCache(int capacity) {
this.hashMap=new HashMap<>();
this.linkedList=new DoubleLinkedList();
this.cacheSize=capacity;
}
// 获取
public int get(int key) {
if(!hashMap.containsKey(key)) {
return -1;
}
int val=hashMap.get(key).val;
put(key,val);
return val;
}
// 存放
public void put(int key,int val) {
Node node=new Node(key, val);
// 判断节点是否已经存在
if(hashMap.containsKey(key)) {
// 删除旧节点,插到链表头
linkedList.remove(hashMap.get(key));
linkedList.addHead(node);
hashMap.put(key, node);
}else {
// 如果节点不存在,判断容器是否已满
if(linkedList.size()==cacheSize) {
// 容器已满,删掉链表最后一个
Node last=linkedList.removeLast();
hashMap.remove(last.key);
}
// 插入链表头
linkedList.addHead(node);
hashMap.put(key, node);
}
}
}
class DoubleLinkedList {
// 虚拟头尾节点
private Node head;
private Node tail;
// 链表元素个数
private int size;
public DoubleLinkedList() {
this.head = new Node(0, 0);
this.tail = new Node(0, 0);
this.size=0;
head.next=tail;
tail.pre=head;
}
// 添加到链表的头部
public void addHead(Node node) {
node.next=head.next;
node.pre=head;
head.next.pre=node;
head.next=node;
size++;
}
// 删除节点
public void remove(Node node) {
node.pre.next=node.next;
node.next.pre=node.pre;
size--;
}
// 删除最后一个节点,并返回
public Node removeLast() {
if(tail.pre==head) {
return null;
}
Node last=tail.pre;
remove(last);
return last;
}
// 返回长度
public int size() {
return size;
}
}
class Node {
public int key;//节点key值
public int val;//节点val值
public Node next;//下一个节点
public Node pre;//前一个节点
public Node(int key, int val) {
this.key = key;
this.val = val;
}
}
标签: 哈希表 双向链表 设计