zoukankan      html  css  js  c++  java
  • [LeetCode] 146. LRU Cache

    Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.

    Implement the LRUCache class:

    • LRUCache(int capacity) Initialize the LRU cache with positive size capacity.
    • int get(int key) Return the value of the key if the key exists, otherwise return -1.
    • void put(int key, int value) Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of keys exceeds the capacity from this operation, evict the least recently used key.

    Follow up:
    Could you do get and put in O(1) time complexity?

    Example 1:

    Input
    ["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
    [[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
    Output
    [null, null, null, 1, null, -1, null, -1, 3, 4]
    
    Explanation
    LRUCache lRUCache = new LRUCache(2);
    lRUCache.put(1, 1); // cache is {1=1}
    lRUCache.put(2, 2); // cache is {1=1, 2=2}
    lRUCache.get(1);    // return 1
    lRUCache.put(3, 3); // LRU key was 2, evicts key 2, cache is {1=1, 3=3}
    lRUCache.get(2);    // returns -1 (not found)
    lRUCache.put(4, 4); // LRU key was 1, evicts key 1, cache is {4=4, 3=3}
    lRUCache.get(1);    // return -1 (not found)
    lRUCache.get(3);    // return 3
    lRUCache.get(4);    // return 4

    Constraints:

    • 1 <= capacity <= 3000
    • 0 <= key <= 3000
    • 0 <= value <= 104
    • At most 3 * 104 calls will be made to get and put.

    LRU缓存器。

    实现这个缓存器,时间复杂度要求是O(1)。LRU缓存器是在一定的容量范围内存了一些node,按照上一次被访问的时间由新到旧排列,越近被访问的node应该排得越靠前。有几个函数需要实现。

    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.

    思路是双向链表(DLL) + hashmap。因为题意要求了时间复杂度必须是O(1)所以只有hashmap才能满足这个时间复杂度。至于为什么是DLL而不是单链表,则是为了node之间的移动方便,也是为了添加删除节点的时候能更加高效。这里解释一下两个函数的实现细节。

    get函数,就是在hashmap里面寻找目标。如果有则先删除这个目标,再加进去,再返回,没有则返回-1;同时DLL里面,如果没有这个目标则什么都不做,如果有这个目标则需要把这个node移动到头节点。

    put函数,如果node之前存在过,则hashmap没有动作,DLL里面需要把这个node放到头节点;如果node不存在,需要看一下此时此刻node的数量是否超过cache的容量capacity,如果没超过就直接把node放到DLL的头节点,放入hashmap并size++,如果超过了capacity,要先去掉DLL的tail节点,再加入当前节点。

    时间O(1) - required

    空间O(n) - hashmap + DLL

    Java实现

     1 class LRUCache {
     2     class Node {
     3         int key;
     4         int value;
     5         Node prev;
     6         Node next;
     7     }
     8 
     9     private void addNode(Node node) {
    10         // always add the node after the head
    11         node.prev = head;
    12         node.next = head.next;
    13         head.next.prev = node;
    14         head.next = node;
    15     }
    16 
    17     private void removeNode(Node node) {
    18         Node prev = node.prev;
    19         Node next = node.next;
    20         prev.next = next;
    21         next.prev = prev;
    22     }
    23 
    24     private void moveToHead(Node node) {
    25         removeNode(node);
    26         addNode(node);
    27     }
    28 
    29     private Node popTail() {
    30         Node res = tail.prev;
    31         removeNode(res);
    32         return res;
    33     }
    34 
    35     private HashMap<Integer, Node> cache = new HashMap<>();
    36     private int size;
    37     private int capacity;
    38     private Node head, tail;
    39 
    40     public LRUCache(int capacity) {
    41         this.size = 0;
    42         this.capacity = capacity;
    43         head = new Node();
    44         tail = new Node();
    45         head.next = tail;
    46         tail.prev = head;
    47     }
    48 
    49     public int get(int key) {
    50         Node node = cache.get(key);
    51         if (node == null) {
    52             return -1;
    53         }
    54         moveToHead(node);
    55         return node.value;
    56     }
    57 
    58     public void put(int key, int value) {
    59         Node node = cache.get(key);
    60         if (node == null) {
    61             Node newNode = new Node();
    62             newNode.key = key;
    63             newNode.value = value;
    64             cache.put(key, newNode);
    65             addNode(newNode);
    66             size++;
    67             if (size > capacity) {
    68                 Node tail = popTail();
    69                 cache.remove(tail.key);
    70                 size--;
    71             }
    72         } else {
    73             node.value = value;
    74             moveToHead(node);
    75         }
    76     }
    77 }
    78 
    79 /**
    80  * Your LRUCache object will be instantiated and called as such:
    81  * LRUCache obj = new LRUCache(capacity);
    82  * int param_1 = obj.get(key);
    83  * obj.put(key,value);
    84  */
    View Code

    JavaScript实现

    ES6里面的hashmap有一个特性,是加入hashmap里面的object是有序的,所以如果用ES6写这道题的话是不需要DLL的。用map.prototype.keys()得到一个迭代器(iterator),然后用.next().value获取到map里面最老的一个节点然后将其删除。我这里给出两种实现,第一种不需要DLL,第二种需要。

     1 /**
     2  * @param {number} capacity
     3  */
     4 var LRUCache = function (capacity) {
     5     this.capacity = capacity;
     6     this.cache = new Map();
     7 };
     8 
     9 /** 
    10  * @param {number} key
    11  * @return {number}
    12  */
    13 LRUCache.prototype.get = function (key) {
    14     if (this.cache.has(key)) {
    15         var val = this.cache.get(key);
    16         this.cache.delete(key);
    17         this.cache.set(key, val);
    18         return val;
    19     } else {
    20         return -1;
    21     }
    22 };
    23 
    24 /** 
    25  * @param {number} key 
    26  * @param {number} value
    27  * @return {void}
    28  */
    29 LRUCache.prototype.put = function (key, value) {
    30     if (this.cache.size < this.capacity && !this.cache.has(key)) {
    31         this.cache.set(key, value);
    32     } else if (this.cache.has(key)) {
    33         this.cache.delete(key);
    34         this.cache.set(key, value);
    35     } else if (this.cache.size === this.capacity) {
    36         // Map.prototype.keys() 返回一个迭代对象,而不是数组
    37         // 迭代对象 Iterator.next() 是迭代对象的第一个对象,而不是值,需要 .value获取值
    38         this.cache.delete(this.cache.keys().next().value);
    39         this.cache.set(key, value);
    40     }
    41 };
    42 
    43 /**
    44  * Your LRUCache object will be instantiated and called as such:
    45  * var obj = new LRUCache(capacity)
    46  * var param_1 = obj.get(key)
    47  * obj.put(key,value)
    48  */
    View Code
      1 /**
      2  * @param {number} capacity
      3  */
      4 const LRUCache = function (capacity) {
      5     this.size = 0;
      6     this.capacity = capacity;
      7     this.LL = new DoublyLinkedList();
      8     this.cache = {};
      9 };
     10 
     11 LRUCache.prototype.get = function (key) {
     12     const getNode = this.cache[key];
     13     if (getNode) {
     14         this.LL.moveToHead(getNode);
     15     } else {
     16         return -1;
     17     }
     18     return getNode.val;
     19 };
     20 
     21 LRUCache.prototype.put = function (key, value) {
     22     // overwrite the value if it exists
     23     if (this.cache[key]) {
     24         this.cache[key].val = value;
     25         this.LL.moveToHead(this.cache[key]);
     26         return;
     27     }
     28 
     29     // otherwise create new node
     30     const node = new Node(key, value);
     31 
     32     // only evict if max capacity has been reached
     33     if (this.size < this.capacity) {
     34         this.size++;
     35     } else {
     36         const keyToRemove = this.LL.removeTail();
     37         delete this.cache[keyToRemove];
     38     }
     39 
     40     // set to most recently used
     41     this.LL.insertHead(node);
     42     this.cache[key] = node;
     43 };
     44 
     45 class Node {
     46     constructor(key, val) {
     47         this.key = key;
     48         this.val = val;
     49         this.next = null;
     50         this.prev = null;
     51     }
     52 }
     53 
     54 class DoublyLinkedList {
     55     constructor() {
     56         this.head = null;
     57         this.tail = null;
     58     }
     59 
     60     removeTail() {
     61         const evict = this.tail;
     62         if (evict.prev !== null && this.tail != this.head) {
     63             evict.prev.next = null;
     64             this.tail = evict.prev;
     65         } else {
     66             this.tail = null;
     67             this.head = null;
     68         }
     69         return evict.key;
     70     }
     71 
     72     insertHead(node) {
     73         if (this.head !== null) {
     74             node.next = this.head;
     75             this.head.prev = node;
     76             this.head = node;
     77         } else {
     78             this.head = node;
     79             this.tail = node;
     80         }
     81     }
     82 
     83     moveToHead(node) {
     84         if (node === this.head) return;
     85         if (node === this.tail) {
     86             node.next = this.head;
     87             this.head.prev = node;
     88             this.tail = node.prev;
     89             node.prev.next = null;
     90             node.prev = null;
     91         } else {
     92             node.prev.next = node.next;
     93             node.next.prev = node.prev;
     94             node.next = this.head;
     95             node.next.prev = node;
     96             node.prev = null
     97         }
     98         this.head = node;
     99     }
    100 }
    101 
    102 /**
    103  * Your LRUCache object will be instantiated and called as such:
    104  * var obj = new LRUCache(capacity)
    105  * var param_1 = obj.get(key)
    106  * obj.put(key,value)
    107  */
    View Code

    LeetCode 题目总结

  • 相关阅读:
    C++/C函数的调用规范
    Computer Science Conference Rankings 计算机科学会议排名 Rank
    Height of CComboBox's drop down list
    java instrument跟踪java freemarker调用过程
    Text to speech hello world sapi
    Visual Studio 插件 代码注释对齐
    #define WINVER 0x0501 之后菜单不显示图标了
    转 用NodeJS打造你的静态文件服务器
    代码注释对齐
    修复MSN上联系人全部显示脱机状态,删除缓存
  • 原文地址:https://www.cnblogs.com/cnoodle/p/12388160.html
Copyright © 2011-2022 走看看