运用你所掌握的数据结构,设计和实现一个 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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题解:
方法一:
用c++的unordered_map + 手写双向链表
链表结构: struct Node{
int key; // 密钥
int val; // 数据值
Node *pre; // 前继结点
Node *next; // 后续结点
Node(int k,int v) : key(k),val(v),pre(NULL),next(NULL) {}
};
unordered_map作用:可以快速的的找到密钥key所对应的value;判断密钥key是否存在
双向链表作用:快速的插入与删除,去除最近最久未使用的结点相等方便,时间复杂度O(1);为什么双向?因为unordered_map存放的key是密钥,value是Node结点,学过数据结构的都知道,中间插入一个结点,需知道该结点的前继结点与后续结点,才能操作。
细节见代码,好久没写链表,生疏了点
class LRUCache {
public:
struct Node{
int key;
int val;
Node *pre;
Node *next;
Node(int k,int v) : key(k),val(v),pre(NULL),next(NULL) {}
};
unordered_map<int,Node*>mapp;
int len1=0,len2=0; // len1:链表总长度,len2:链表当前长度
Node *rt = NULL; // 头结点
Node *r = NULL; // 始终指向最后一个结点
LRUCache(int capacity) {
len1=capacity;
rt = new Node(-1,-1);
r=rt;
}
void pushBack(Node* l,int key){ // 用过的结点放到链表最后
if(l->next!=NULL){
l->next->pre=l->pre;
l->pre->next=l->next;
l->pre=r;
r->next=l;
l->next=NULL;
r=l;
mapp[key]=r;
}
}
int get(int key) {
if(!len1) return -1;
if(!mapp.count(key)) return -1;
else{
Node* l=mapp[key];
pushBack(l, key);
return l->val;
}
}
void put(int key, int value) {
if(!len1) return ;
if(mapp.count(key)){
mapp[key]->val=value;
Node* l=mapp[key];
pushBack(l, key);
return ;
}
Node* p=new Node(key,value); // 从表尾插入新结点
r->next=p;
p->pre=r;
r=p;
mapp[key]=r;
if(len2>=len1){ // 集合已满,删除链表首部结点
Node* l=rt->next;
int k=l->key;
rt->next=l->next;
l->next->pre=rt;
l->pre=NULL;
l->next=NULL;
mapp.erase(k);
}else{
len2++;
}
}
};
方法二:java中LinkedHashMap实现了LRU
class LRUCache {
Map<Integer,Integer> linkedHashMap = null;
int len=0;
public LRUCache(int capacity) {
linkedHashMap = new LinkedHashMap(capacity, 0.75f,true);
len = capacity;
}
public int get(int key) {
if(!linkedHashMap.containsKey(key)) return -1;
return linkedHashMap.get(key);
}
public void put(int key, int value) {
if(linkedHashMap.containsKey(key)){
linkedHashMap.put(key,value);
return ;
}
if(linkedHashMap.size()>=len){
int k=0;
for(Map.Entry<Integer,Integer> en : linkedHashMap.entrySet()){
if(en!=null){
k=en.getKey();
break;
}
}
linkedHashMap.remove(k);
}
linkedHashMap.put(key,value);
}
}