为最近最少使用(LRU)缓存策略设计一个数据结构,它应该支持以下操作:获取数据(get)和写入数据(set)。
获取数据get(key):如果缓存中存在key,则获取其数据值(通常是正数),否则返回-1。
写入数据set(key, value):如果key还没有在缓存中,则写入其数据值。当缓存达到上限,它应该在写入新数据之前删除最近最少使用的数据用来腾出空闲位置。
用一个链表来表示全部的缓存数据,显然只需要将最近的访问过的结点移到队尾,那么头部自然就是没有访问过的结点。因为需要经常移动结点,从时间上来考虑使用双向链表是很好的选择.
节点中包含键值对,hashmap存储key和val,通过在map中查找key来定位结点是比较方便的做法
总结:用双向链表来表示结点关系,用map来存储键值对.
public class Solution {
public class Node{
Node pre;
Node next;
int key;
int val;
public Node(int key, int val){
this.key = key;
this.val = val;
this.pre = null;
this.next = null;
}
}
public int capacity;
public HashMap<Integer, Node> map = new HashMap<Integer, Node>();
public Node head = new Node(-1,-1);
public Node tail = new Node(-1,-1);
// @param capacity, an integer
public Solution(int capacity) {
this.capacity = capacity;
head.next = tail;
tail.pre = head;
}
// @return an integer
public int get(int key) {
if(!map.containsKey(key)){ //不存在该结点
return -1;
}
Node current = map.get(key);
current.next.pre = current.pre; //最近访问过的结点放到队尾
current.pre.next = current.next;
move_to_tail(current);
return map.get(key).val;
}
// @param key, an integer
// @param value, an integer
// @return nothing
public void set(int key, int value) {
if(get(key) != -1){ //缓存中已经包含结点
Node node = map.get(key); //取下结点
node.pre.next = node.next;
node.next.pre = node.pre;
move_to_tail(node); //移到队尾
map.get(key).val = value; //新的值
return;
}
if(map.size() == capacity){ //缓存已满
map.remove(head.next.key);
head.next = head.next.next; //移除头
head.next.pre = head;
}
Node insert = new Node(key,value);
move_to_tail(insert);
map.put(key,insert); //添加新的结点
}
//移动结点到队尾
public void move_to_tail(Node node){
node.pre = tail.pre;
tail.pre.next = node;
node.next = tail;
tail.pre = node;
}
}