LruCache的两种代码实现

237 阅读1分钟

最近看了一些面经,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%的用户

目前没有找到效率比第二种更高的方法,有其它想法会继续补充。