LRU Cache
Question
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.
get(key)
- Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
set(key, value)
- Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.
Analysis:
The basic idea is using LinkedList + HashMap
HashMap
is used to store the key-value
pairs and LinkedList
is used as a queue to record the order of recent used key-value
We can implement our own LinkedList structure or using LinkedHashMap
from Java Collection.
Solution Using LinkedHashMap
public class LRUCache {
private int capacity;
private LinkedHashMap<Integer,Integer> map;
public LRUCache(int capacity) {
this.capacity=capacity;
this.map=new LinkedHashMap<Integer,Integer>();
}
public int get(int key) {
if(map.containsKey(key)) {
int res=map.get(key);
map.remove(key);
map.put(key,res);
return res;
}
else return -1;
}
public void set(int key, int value) {
if(map.containsKey(key)) map.remove(key);
if(capacity>0){
if(map.size()==capacity) map.remove(map.keySet().iterator().next());
map.put(key,value);
}
}
}
Solution Implemented LinkedList
public class LRUCache {
class Node{
int key;
int value;
Node next, pre;
Node(int key, int value){
this.key=key;
this.value=value;
}
}
Node cache;//head node of the cache
HashMap<Integer,Node> dic;
int capacity;
public LRUCache(int capacity) {
cache=null;
dic=new HashMap<Integer,Node>();
this.capacity=capacity;
}
public int get(int key) {
if(dic.containsKey(key)) {
flushCache(key);
return dic.get(key).value;
}
else return -1;
}
public void set(int key, int value) {
if(capacity<=0) return;
if(dic.containsKey(key)) {
dic.get(key).value=value;
flushCache(key);
}else{
if(dic.size()==capacity){//when cache is full, evict the least recently used node
if(cache.pre.equals(cache)){//when there is only one node
dic.remove(cache.key);
cache=null;
}else{
dic.remove(cache.pre.key);
cache.pre.pre.next=null;
cache.pre=cache.pre.pre;
}
}
//append the new node
Node head=new Node(key,value);
if(cache==null){
head.pre=head;
head.next=null;
cache=head;
}else{
head.pre=cache.pre;
cache.pre=head;
head.next=cache;
cache=head;
}
dic.put(key,head);
}
}
public void flushCache(int key){//flush cache means to update the state of the recently used nodec
Node toBeHead=dic.get(key);
if(!toBeHead.equals(cache)){
toBeHead.pre.next=toBeHead.next;
if(toBeHead.next!=null) toBeHead.next.pre=toBeHead.pre;
toBeHead.pre=(toBeHead.next==null)?toBeHead.pre:cache.pre;
cache.pre=toBeHead;
toBeHead.next=cache;
cache=toBeHead;
}
}
}
Python Version
from collections import OrderedDict
class LRUcache(object):
def __init__(self, capacity):
self.capacity = capacity
self.cache_map = OrderedDict()
def get(self, k):
if k in self.cache_map:
ans = self.cache_map[k]
del self.cache_map[k]
self.cache_map[k] = ans
return ans
else:
return -1
def set(self, k, v):
if k in self.cache_map:
del self.cache_map[k]
if len(self.cache_map) == self.capacity:
self.cache_map.popitem(last=False)
self.cache_map[k] = v