https://leetcode.com/problems/lru-cache/
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.
需要注意的是,在get和set操作后,被操作的节点就应该是最新用过的节点,应该移到最后面。
另外,需要注意linked list的操作,比如删除头节点,尾节点,capacity只有1这种情况都应该考虑到。
public class LRUCache {
private int capacity;
private int size;
private Node first;
private Node last;
private HashMap<Integer, Node> map;
class Node{
int key;
int value;
Node prev, next;
public Node(int key, int val){
this.key = key;
this.value = val;
prev = null;
next = null;
}
}
public LRUCache(int capacity) {
this.map = new HashMap<Integer, Node>();
this.capacity = capacity;
this.size = 0;
}
public int get(int key) {
if(map.containsKey(key)){
Node current = map.get(key);
if(current!=last){
if(current==first){
first = first.next;
first.prev = null;
}
else{
current.next.prev = current.prev;
current.prev.next = current.next;
}
last.next = current;
current.prev = last;
current.next = null;
last = current;
}
return current.value;
}
return -1;
}
public void set(int key, int value) {
if(map.containsKey(key)){
Node current = map.get(key);
if(current!=last){
if(current==first){
first = first.next;
if(first!=null) first.prev = null;
}
else{
current.next.prev = current.prev;
current.prev.next = current.next;
}
last.next = current;
current.prev = last;
current.next = null;
last = current;
}
current.value = value;
}
else{
Node current = new Node(key, value);
map.put(key, current);
if(size<capacity){
if(first==null){
first = current;
last = current;
}
else{
current.prev = last;
last.next = current;
last = last.next;
}
size++;
}
else{
Node tmp = first;
first = first.next;
if(first!=null) first.prev = null;
if(first==null){
first = current;
last = current;
}
else{
current.prev = last;
last.next = current;
last = last.next;
}
map.remove(tmp.key);
}
}
}
}
上面的代码有很多重复代码,比如删除节点之类的,所以,可以有两个remove和add函数,remove函数删除任意一个节点,add函数把一个节点加到list的末尾,两个函数都相应地更新size和hashmap。代码如下:
public class LRUCache {
private int capacity;
private int size;
private Node first;
private Node last;
private HashMap<Integer, Node> map;
class Node{
int key;
int value;
Node prev, next;
public Node(int key, int val){
this.key = key;
this.value = val;
prev = null;
next = null;
}
}
public LRUCache(int capacity) {
this.map = new HashMap<Integer, Node>();
this.capacity = capacity;
this.size = 0;
}
public void remove(Node node){
if(node==first){
first = first.next;
if(first!=null) first.prev = null;
else last=null;
}
else if(node == last){
last = last.prev;
if(last!=null) last.next = null;
else first = null;
}
else{
node.prev.next = node.next;
node.next.prev = node.prev;
}
map.remove(node.key);
size--;
}
public void add(Node node){
if(first==null || last==null){
last = node;
first = node;
}
else{
last.next = node;
node.prev = last;
last = last.next;
}
map.put(node.key, node);
size++;
}
public int get(int key) {
if(map.containsKey(key)){
Node current = map.get(key);
if(current!=last){
remove(current);
add(current);
}
return current.value;
}
return -1;
}
public void set(int key, int value) {
if(map.containsKey(key)){
Node current = map.get(key);
if(current!=last){
remove(current);
add(current);
}
current.value = value;
}
else{
Node current = new Node(key, value);
if(size>=capacity) remove(first);
add(current);
}
}
}
以下是C++的版本:
第一个版本是自己定义list,自己定义list node,然后用unordered_map做的,注意,这里所有在JAVA里面直接保存的node引用都要变成node指针。即全部是指针操作了, 并且在这里所有的node都是用new定义的,不然node的有效范围就只是当前函数模块,但是用node定义的话,在彻底删除节点时,需要delete这个node:
class Node{
public:
int key;
int value;
Node* prev;
Node* next;
Node(int k, int v){
this->key = k;
this->value = v;
prev = NULL;
next = NULL;
}
};
class LRUCache{
private:
unordered_map<int, Node*> map;
Node* first;
Node* last;
int capacity;
int size;
public:
LRUCache(int capacity) {
first = NULL;
last = NULL;
this->capacity = capacity;
this->size = 0;
}
void removeNode(Node* node){
if(node==first){
first = first->next;
if(first!=NULL) first->prev = NULL;
if(first==NULL) last = NULL;
}
else if(node==last){
last = last->prev;
if(last!=NULL) last->next = NULL;
if(last==NULL) first = NULL;
}
else{
node->prev->next = node->next;
node->next->prev = node->prev;
}
size--;
map.erase(node->key);
}
void add(Node* node){
if(first == NULL){
first = node;
last = node;
}
else{
last->next = node;
node->prev = last;
last = last->next;
}
size++;
map[node->key] = node;
}
int get(int key) {
if(map.find(key)!=map.end()){
Node* current = map[key];
removeNode(current);
add(current);
return current->value;
}
else return -1;
}
void set(int key, int value) {
if(map.find(key)!=map.end()){
Node* current = map[key];
removeNode(current);
add(current);
current->value = value;
}
else{
if(size==capacity){
Node* tdl = first;
removeNode(tdl);
delete tdl;
}
Node* current = new Node(key, value);
add(current);
}
}
};
第二个版本是用STL里面的list做的,因为STL里面list是双向链表,forward_list是单向链表,所以不用自己定义list,直接用STL提供的模板做,注意iterator是指向模板内节点的指针:
class Node{
public:
int key;
int value;
Node(int k, int v) : key(k), value(v){}
};
class LRUCache{
private:
unordered_map<int, list<Node>::iterator> map;
list<Node> cache;
int capacity;
public:
LRUCache(int capacity) {
this->capacity = capacity;
}
void MoveToHead(int key){
Node update = *map[key];
cache.erase(map[key]);
cache.push_front(update);
map[key] = cache.begin();
}
int get(int key) {
if(map.find(key)!=map.end()){
MoveToHead(key);
return map[key]->value;
}
else return -1;
}
void set(int key, int value) {
if(map.find(key)!=map.end()){
MoveToHead(key);
map[key]->value = value;
}
else{
if(cache.size()==capacity){
map.erase(cache.back().key);
cache.pop_back();
}
Node current(key, value);
cache.push_front(current);
map[key] = cache.begin();
}
}
};