[LeetCode] 146. LRU Cache 近期最少使用缓存

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),实现get(key), set(key, value)功能。

get(key):取值(key恒为正), 不存在时返回-1。如果存在,返回值,并且delete此key,在从新写入cache,因为要最近刚使用过,要把它放到队尾。
set(key, value):缓存已满,删除近期最久未被使用的节点,添加新节点进缓存。缓存未满,节点存在,修改value;节点不存在,添加新节点进缓存;最后更新此节点到队尾。

解法1: 双向链表(Doubly-Linked List) + HashMap

双向链表:维护缓存节点CacheNode,凡是被访问(新建/修改命中/访问命中)过的节点,一律在访问完成后移动到双向链表尾部,保证链表尾部始终为最新节点;保证链表头部始终为最旧节点,LRU策略删除时表现为删除双向链表头部;由于链表不支持随机访问,使用HashMap+双向链表实现LRU缓存,HashMap中键值对:<key, CacheNode>。

解法2: OrderedDict有序字典

Time: Get O(1) Set O(1), Space: O(N)

Java:

import java.util.HashMap;

class Solution {
    private HashMap<Integer, CacheNode> map;
    private int capacity;
    // head.next和tail.next指向链表头尾,包起来防止null
    private CacheNode head = new CacheNode(-1, -1);
    private CacheNode tail = new CacheNode(-1, -1);
    
    private class CacheNode {
        int key, value;
        CacheNode pre, next;
        CacheNode(int key, int value) {
            this.key = key;
            this.value = value;
            this.pre = null;
            this.next = null;
        }
    }
    
    public Solution(int capacity) {
        this.map = new HashMap<>();
        this.capacity = capacity;
    }
    
    // 将已有节点或新建节点移动到链表尾部
    private void moveToTail(CacheNode target, boolean isNew) {
        // 尾部节点显然不需要移动
        if (target != tail.next) {
            if (!isNew) {
                // 修改旧节点的双向链表指针
                target.pre.next = target.next; 
                target.next.pre = target.pre;
            }
            // 添加节点到链表尾部
            tail.next.next = target;
            target.pre = tail.next;
            tail.next = target;
        }
    }
    
    // 命中节点添加到链表尾部,未命中返回-1
    public int get(int key) {
        if (map.containsKey(key)) {
            CacheNode target = map.get(key);
            // 将已有节点移动到链表尾部
            moveToTail(target, false);
            // 此时链表尾部tail.next = target,更新next指向null,防止出现环
            tail.next.next = null;
            return target.value;
        }
        return -1;
    }
    
    public void set(int key, int value) {
        if (map.containsKey(key)) {
            CacheNode target = map.get(key);
            target.value = value;
            map.put(key, target);
            // 将访问过的已有节点移动到链表尾部
            moveToTail(target, false);
        } else if(map.size() < capacity) {    // cache未满,添加节点
            CacheNode newNode = new CacheNode(key, value);
            map.put(key, newNode);
            if (head.next == null) {
                head.next = newNode;
                newNode.pre = head;
                tail.next = newNode;
            } else {
                // 将新建节点移动到链表尾部
                moveToTail(newNode, true);
            }
        } else {    // cache已满,淘汰链表链表头部节点,新节点加入到链表尾部
            CacheNode newNode = new CacheNode(key, value);
            map.remove(head.next.key);
            map.put(key, newNode);
            // cache中只有一个元素
            if (head.next == tail.next) {
                head.next = newNode;
                tail.next = newNode;
            } else {    // cache中不止一个元素,删除头部
                head.next.next.pre = head; // 更新新头部.pre = head
                head.next = head.next.next;// 更新新头部
                // 将新建节点移动到链表尾部
                moveToTail(newNode, true);
            }
        }
    }
}

Python:

class Node:
    def __init__(self, key, val):
        self.key = key
        self.val = val
        self.prev = None
        self.next = None

class LRUCache:
    # @param capacity, an integer
    def __init__(self, capacity):
        self.capacity = capacity
        self.size = 0
        self.dummyNode = Node(-1, -1)
        self.tail = self.dummyNode
        self.entryFinder = {}

    # @return an integer
    def get(self, key):
        entry = self.entryFinder.get(key)
        if entry is None:
            return -1
        else:
            self.renew(entry)
            return entry.val

    # @param key, an integer
    # @param value, an integer
    # @return nothing
    def set(self, key, value):
        entry = self.entryFinder.get(key)
        if entry is None:
            entry = Node(key, value)
            self.entryFinder[key] = entry
            self.tail.next = entry
            entry.prev = self.tail
            self.tail = entry
            if self.size < self.capacity:
                self.size += 1
            else:
                headNode = self.dummyNode.next
                if headNode is not None:
                    self.dummyNode.next = headNode.next
                    headNode.next.prev = self.dummyNode
                del self.entryFinder[headNode.key]
        else:
            entry.val = value
            self.renew(entry)
    
    def renew(self, entry):
        if self.tail != entry:
            prevNode = entry.prev
            nextNode = entry.next
            prevNode.next = nextNode
            nextNode.prev = prevNode
            entry.next = None
            self.tail.next = entry
            entry.prev = self.tail
            self.tail = entry

Python: 

class ListNode(object):
    def __init__(self, key, val):
        self.val = val
        self.key = key
        self.next = None
        self.prev = None

class LinkedList(object):
    def __init__(self):
        self.head = None
        self.tail = None
    
    def insert(self, node):
        node.next, node.prev = None, None  # avoid dirty node
        if self.head is None:
            self.head = node
        else:
            self.tail.next = node
            node.prev = self.tail
        self.tail = node
            
    def delete(self, node):
        if node.prev:
            node.prev.next = node.next
        else:
            self.head = node.next
        if node.next:
            node.next.prev = node.prev
        else:
            self.tail = node.prev
        node.next, node.prev = None, None  # make node clean

class LRUCache(object):

    def __init__(self, capacity):
        self.list = LinkedList()
        self.dict = {}
        self.capacity = capacity
        
    def _insert(self, key, val):
        node = ListNode(key, val)
        self.list.insert(node)
        self.dict[key] = node
        
    def get(self, key):
        if key in self.dict:
            val = self.dict[key].val
            self.list.delete(self.dict[key])
            self._insert(key, val)
            return val
        return -1
        
    def set(self, key, val):
        if key in self.dict:
            self.list.delete(self.dict[key])
        elif len(self.dict) == self.capacity:
            del self.dict[self.list.head.key]
            self.list.delete(self.list.head)
        self._insert(key, val)

Python:

class LRUCache:

    def __init__(self, capacity):
        self.capacity = capacity
        self.cache = collections.OrderedDict()

    def get(self, key):
        if not key in self.cache:
            return -1
        value = self.cache.pop(key)
        self.cache[key] = value
        return value

    def set(self, key, value):
        if key in self.cache:
            self.cache.pop(key)
        elif len(self.cache) == self.capacity:
            self.cache.popitem(last=False)
        self.cache[key] = value

C++:

class LRUCache{
public:
    LRUCache(int capacity) {
        cap = capacity;
    }
    
    int get(int key) {
        auto it = m.find(key);
        if (it == m.end()) return -1;
        l.splice(l.begin(), l, it->second);
        return it->second->second;
    }
    
    void set(int key, int value) {
        auto it = m.find(key);
        if (it != m.end()) l.erase(it->second);
        l.push_front(make_pair(key, value));
        m[key] = l.begin();
        if (m.size() > cap) {
            int k = l.rbegin()->first;
            l.pop_back();
            m.erase(k);
        }
    }
    
private:
    int cap;
    list<pair<int, int> > l;
    unordered_map<int, list<pair<int, int> >::iterator> m;
};

 

All LeetCode Questions List 题目汇总

  

 

posted @ 2018-03-06 08:36  轻风舞动  阅读(669)  评论(0编辑  收藏  举报