LeetCode | LRU Cache

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

Posted by wzystal on June 10, 2014

原题描述

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.
get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
set(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.

解题思路

题目要求算法能根据键值对随机存取缓存,我们很容易就能想到使用哈希表来实现。但是题目还要求缓存是LRU(Least Recently Used)形式的,即在更新缓存时采用最近最少使用原则,这就需要数据结构能记录缓存的命中情况:每次有缓存被命中都需要将其标记为最近使用,当缓存大小超出容量时,需要删除最久未被使用的缓存。
在Java中,正好有这么一种哈希表,可以记录数据的访问顺序,它就是LinkedHashMap。借助于LinkedHashMap,我们可以很容易地实现LRU Cache。实现代码如下:

import java.util.LinkedHashMap;  
  
public class LRUCache extends LinkedHashMap<Integer, Integer> {  
    private int capacity;  
  
    public LRUCache1(int capacity) {  
        super(capacity+1, 0.75f, true);  
        this.capacity = capacity;  
    }  
  
    @Override  
    protected boolean removeEldestEntry(  
            java.util.Map.Entry<Integer, Integer> eldest) {  
        return size() > capacity; //当缓存大小超出容量时,移除最久未被使用的缓存  
    }  
  
    public int get(int key) {  
        return super.get(key) != null ? super.get(key) : -1;  
    }  
  
    public void set(int key, int value) {  
        super.put(key, value);  
    }  
  
    public int size() {  
        return super.size();  
    }  
  
}  

LinkedHashMap的方案虽然简便,但是它毕竟不是基本的数据结构,从算法设计的角度来说,我们还是需要一个使用基本数据结构来实现的方案。参考LinkedHashMap类的内部实现原理,我们可以通过哈希表+双链表的组合来实现LRU Cache。由哈希表来实现根据键值对(值为双链表结点)随机存取,由双链表来维护缓存的命中情况:最近使用的缓存在链表头结点,最久未使用的缓存在链表尾结点,当缓存被命中时,将其移至链表头结点,当缓存大小超出容量时,直接删除链表尾结点。代码实现如下:

public class LRUCache {  
  
    class CacheNode {  
        public int key;  
        public int value;  
        public CacheNode pre;  
        public CacheNode next;  
  
        public CacheNode(int key, int value) {  
            this.key = key;  
            this.value = value;  
        }  
    }  
  
    private int capacity;  
    private int size = 0;  
    private HashMap<Integer, CacheNode> map = new HashMap<>();  
    private CacheNode head;  
    private CacheNode tail;  
  
    public LRUCache(int capacity) {  
        this.capacity = capacity;  
        head = null;  
        tail = null;  
    }  
  
    private void removeNode(CacheNode node) {  
        CacheNode pre = node.pre;  
        CacheNode next = node.next;  
        if (pre != null) {  
            pre.next = next;  
        } else {  
            head = next;  
        }  
        if (next != null) {  
            next.pre = pre;  
        } else {  
            tail = pre;  
        }  
    }  
  
    private void setHead(CacheNode node) {  
        node.next = head;  
        node.pre = null;  
        if (head != null) {  
            head.pre = node;  
        }  
        head = node;  
        if (tail == null) {  
            tail = node;  
        }  
    }  
  
    public void set(int key, int value) {  
        if (map.containsKey(key)) {  
            CacheNode oldNode = map.get(key);  
            oldNode.value = value;  
            removeNode(oldNode);  
            setHead(oldNode);  
        } else {  
            CacheNode newNode = new CacheNode(key, value);  
            if (size < capacity) {  
                size++;  
            } else {  
                map.remove(tail.key);  
                tail = tail.pre;  
                if (tail != null) {  
                    tail.next = null;  
                }  
            }  
            setHead(newNode);  
            map.put(key, newNode);  
        }  
    }  
  
    public int get(int key) {  
        if (map.containsKey(key)) {  
            CacheNode lastest = map.get(key);  
            removeNode(lastest);  
            setHead(lastest);  
            return lastest.value;  
        } else {  
            return -1;  
        }  
    }  
  
}