LRU Cache

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return-1.
put(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.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

LRUCache cache = new LRUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
cache.get(2);       // returns -1 (not found)
cache.put(4, 4);    // evicts key 1
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4
思路

双向链表(Doubly Linked List) + HashMap
HashMap: key,value(ListNode)
LinkedList: 存的是按照访问节点的时间顺序排列的ListNode

  1. LRU的实质是按照节点访问时间顺序排列的双向链表。每个双向链表节点有prev和next指向其一前一后的节点。
  2. 最近被访问的节点放置在链表头部。一旦链表达到容量,需要剔除元素的时候,踢出链表尾部的最老时间被访问过的元素。
  3. HashMap则是用来根据key来找对应的链表节点。

Note:

  1. set时,如果该key存在,那么也说明它也是被最近访问过,也需要将其移动到链表最头上。
  2. get时,变到最前端的方法是先删除这个最后的节点,再将其放到最前端。分两步操作
class LRUCache {
    private class ListNode {
        int key;
        int value;
        ListNode prev;
        ListNode next;
        public ListNode(int key, int value) {
            this.key = key;
            this.value = value;
            this.prev = null;
            this.next = null;
        }
    }
    
    private int capacity;
    private HashMap mapping;
    private ListNode head;
    private ListNode tail;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.mapping = new HashMap();
        this.head = new ListNode(-1, -1);
        this.tail = new ListNode(-1, -1);
        head.next = tail;
        tail.next = head;
    }
    
    public int get(int key) {
        //get说明最新访问了它,那么需要将这个节点移动到链表最前端,保证是最新访问过的
        if (!mapping.containsKey(key)){
            return -1;
        }
        
        ListNode cur = mapping.get(key);
        //变到最前端的方法是先删除这个最后的节点,再将其放到最前端。分两步操作
        removeNodeAtEnd(cur);
        moveToHead(cur);
        return cur.value;
    }
    
    public void put(int key, int value) {
        //如果在put的时候发现是有这个node,1. 表示最新访问了,那么在get()中已经将其移动到了最前端
        // 同时,需要更新其value
        if (get(key) != -1) {
            mapping.get(key).value = value;
            return;
        }
        
        //如果put的时候发现cache已满,那么则需要删除链表中最后的那个节点,同时删除掉hashmap中对应的那个NODE
        if (mapping.size() == capacity) {
            //顺序不能错,如果先删除了节点,那么在mapping中找tail.prev就会找错
            mapping.remove(tail.prev.key);
            removeNodeAtEnd(tail.prev);
        }
        //上面操作保证了put的时候,不会出现超过容量的现象
        ListNode newNode = new ListNode(key, value);
        moveToHead(newNode);
        mapping.put(key, newNode);
    }
    
    public void moveToHead(ListNode cur) {
        cur.next = head.next;
        head.next.prev = cur;
        head.next = cur;
        cur.prev = head; 
    }
    
    public void removeNodeAtEnd(ListNode cur) {
        cur.prev.next = cur.next;
        cur.next.prev = cur.prev;
    }
}

/**
 * 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);
 */

你可能感兴趣的:(LRU Cache)