• [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 题目汇总

      

  • 相关阅读:
    对函数的推广
    自然语言也支持泛型
    用委托实现对List的常用方法提取
    C#中的特性(Attributes)(翻译)(转)
    用JQUERY增删元素
    表格折叠展开
    下一代编程语言可能具有的特点
    一种多继承方案
    几年前毕业设计做的CAD二次开发
    数据结构 003.1.1 栈的基本概念
  • 原文地址:https://www.cnblogs.com/lightwindy/p/8512299.html
Copyright © 2020-2023  润新知