最近看了一些面经,LruCache作为一道经典的medium算法题,现在面试时候会经常要求手撕,面试官往往会要求面试者优化算法以提高效率。这里提供两种设计。
注:大部分面试官不会让面试者使用Java的内部实现类,所以本文不考虑LinkedHashMap。
1.LinkedList+HashMap
思路:
public class LruCache {
private int capacity;
private HashMap<Integer, Integer> map;
private LinkedList<Integer> list;
public LruCache(int capacity) {
this.capacity=capacity;
this.map=new HashMap<>();
this.list=new LinkedList<>();
}
public int get(int key) {
if(!map.containsKey(key))return -1;
list.remove((Integer)key);
list.addLast(key);
return map.get(key);
}
public void put(int key,int value) {
if(map.size()==capacity) {
map.remove(list.getFirst());
list.removeFirst();
}
if(!map.containsKey(key)){
map.put(key, value);
list.addLast(key);
}else {
map.put(key, value);
list.remove((Integer)key);
list.addLast(key);
}
}
}
执行结果:
执行用时:177 ms, 在所有 Java 提交中击败了25.84%的用户
内存消耗:47.8 MB, 在所有 Java 提交中击败了66.45%的用户
2.HashMap+自定义双向节点:
思路:
class LRUCache {
int capacity;
HashMap<Integer,LruNode> map;
//这里的这两个节点都是定位的,非实际数据节点
LruNode first;
LruNode last;
public LRUCache(int capacity) {
this.capacity=capacity;
map=new HashMap<>();
first=new LruNode(-1,0);
last=new LruNode(-1,0);
first.next=last;
last.pre=first;
}
public int get(int key) {
if(!map.containsKey(key))return -1;
LruNode node=map.get(key);
deleteNode(node);
updateLast(node);
map.put(key,node);
return node.value;
}
public void put(int key,int value) {
if(map.containsKey(key)){
LruNode node= map.get(key);
deleteNode(node);
updateLast(node);
node.value=value;
map.put(key,node);
}else{
if(map.size()==capacity){
LruNode temp=first.next;
first.next=temp.next;
temp.next.pre=first;
map.remove(temp.key,temp);
LruNode node=new LruNode(key,value);
updateLast(node);
map.put(key,node);
}else{
LruNode node=new LruNode(key,value);
updateLast(node);
map.put(key,node);
}
}
}
public void updateLast(LruNode node) {
last.pre.next=node;
node.pre=last.pre;
node.next=last;
last.pre=node;
}
public void deleteNode(LruNode node) {
node.pre.next=node.next;
node.next.pre=node.pre;
}
class LruNode{
int key;
int value;
LruNode pre;
LruNode next;
public LruNode(int key,int value){
this.value=value;
this.key=key;
}
}
}
执行结果:
执行用时:33 ms, 在所有 Java 提交中击败了70.72%的用户
内存消耗:47.6 MB, 在所有 Java 提交中击败了81.07%的用户
采用内部类实现的执行结果介于两者之间:
执行用时:40 ms, 在所有 Java 提交中击败了39.43%的用户
内存消耗:48.2 MB, 在所有 Java 提交中击败了18.71%的用户
目前没有找到效率比第二种更高的方法,有其它想法会继续补充。