LRU 缓存机制

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 写入数据 put(key, value) -
如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。

进阶:

你是否可以在 O(1) 时间复杂度内完成这两种操作?

思路一:
通过一个哈希表,可以实现get和put时间复杂度为O(1)。
此时,利用java的LinkedHashMap可以实现有序的HashMap,可以设定其按照插入顺序或者访问顺序排序,按照访问顺序来排序恰好满足本题的要求。

  • 时间复杂度:O(1),有序字典中所有操作均为O(1)。
  • 空间复杂度:O(capicity),因为空间只用于有序字典存储最多 capacity + 1 个元素。
class LRUCache extends LinkedHashMap<Integer,Integer>{
    private int capacity;
    public LRUCache(int capacity) { //capicity为HashMap初始容量
        super(capacity,0.75F,true); //accessOrder设置为true意味着按访问顺序排序
        this.capacity = capacity;
    }
    
    public int get(int key) {
        return super.getOrDefault(key,-1); //获取不到则返回-1
    }
    
    public void put(int key, int value) {
        super.put(key,value);
    }

    /**
    *重写父类的方法,在size()>capicity时删除最老的Entry 
    **/
    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest){
        return size()>capacity;
    }


/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */
}

思路二:
用别人的数据结构不能显示水平。所以可以自己实现一个LinkedHashMap。
LinkedHashMap的原理就是一个HashMap加一个双向链表。哈希表用来根据key找到对应节点,双向链表用来表示顺序。
注意:哈希表中存放的是key和对应的节点,value值存放在节点中。
时间空间复杂度和思路一一样。

class LRUCache {
    private int capacity;
    private int size;
    private DLinkNode head,tail; //双向链表的伪头尾节点
    private HashMap<Integer,DLinkNode> map;

    class DLinkNode{  //双向链表的节点类
        int key;
        int value;
        DLinkNode prev;
        DLinkNode next;
    }

    //插入节点,始终插在双向链表头节点后
    public void addNode(DLinkNode node){
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }

    //删除某个节点
    public void removeNode(DLinkNode node){
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    //将某个节点移动到头节点后
    public void moveToAhead(DLinkNode node){
        removeNode(node);
        addNode(node);
    }

    //去掉最尾部的节点,并返回,用以在map中删除它
    public DLinkNode popTail(){
        DLinkNode res = tail.prev;
        removeNode(tail.prev);
        return res;
    }

    //LRUCache的构造函数
    public LRUCache(int capacity) { 
        this.capacity = capacity;
        size = 0;
        head = new DLinkNode();
        tail = new DLinkNode();
        head.next = tail;   //头尾互相指
        tail.prev = head;
        map = new HashMap<Integer,DLinkNode>();
    }
    
    public int get(int key) {
        DLinkNode node = map.get(key);
        if(node==null){
            return -1;
        }
        moveToAhead(node);  //get到之后放到头部
        return node.value;
    }
    
    public void put(int key, int value) {
        //先判断是否已经存在
        if(map.get(key)==null){ //当不存在时,新建一个放进去
            DLinkNode node = new DLinkNode();
            node.key = key;
            node.value = value;
            addNode(node);
            map.put(key,node);
            size++;
            if(size>capacity){  //是否已经满了
                DLinkNode tailNode = popTail();
                map.remove(tailNode.key);
                size--;
            }
        }else{
            DLinkNode node = map.get(key);
            node.value = value;
            map.put(key,node);
            moveToAhead(node);  //更新也要放到头部
        }
    }
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

代码很长,细节拉满,累死我了。

发布了9 篇原创文章 · 获赞 0 · 访问量 121

猜你喜欢

转载自blog.csdn.net/DLC990319/article/details/104484633