• LRU缓存机制(Python and C++解法)


    题目:

    运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。

    获取数据 get(key) - 如果关键字 (key) 存在于缓存中,则获取关键字的值(总是正数),否则返回 -1。
    写入数据 put(key, value) - 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字/值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

    示例:

    LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

    cache.put(1, 1);
    cache.put(2, 2);
    cache.get(1); // 返回 1
    cache.put(3, 3); // 该操作会使得关键字 2 作废
    cache.get(2); // 返回 -1 (未找到)
    cache.put(4, 4); // 该操作会使得关键字 1 作废
    cache.get(1); // 返回 -1 (未找到)
    cache.get(3); // 返回 3
    cache.get(4); // 返回 4

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/lru-cache

    思路:

    设计的必要条件:
      1.缓存中的元素必须有时序,以区分最近使用的和久未使用的数据,当容量满了之后要删除最久未使用的那个元素,腾出位置。
      2.要在缓存中快速找某个key是否已存在并得到对应的value;
      3.每次访问缓存中的某个key,需要将这个元素变为最近使用的,也就是说缓存要支持在任意位置快速插入和删除元素。
    设计:
      哈希表查找快,但是数据无固定顺序;双向链表有顺序之分,插入删除快,但是查找慢。所以结合形成一种新的数据结构:哈希链表。LRU缓存算法的核心数据结构就是哈希链表,双向链表和哈希表的结合体。

      每次默认从链表头部添加元素,那么越靠头部的元素就是最近使用的,越靠尾部的元素就是最久未使用的。
      对于某一个key,可以通过哈希表快速定位到链表中的节点,从而取得对应value。
      双向链表可以获得前驱指针,支持在任意位置快速插入和删除,但是链表无法按照索引快速访问某一个位置的元素,而借助哈希表,可以通过key快速映射到任意一个链表节点,然后进行插入和删除。
      哈希表中已经存储key,链表中还要存储key的原因:当缓存已满,不仅要删除链表节点,还要把哈希表中映射到该节点的key同时删除,而这个key只能由链表节点得到,如果链表节点中只存储value,那么就无法得知key,就无法删除哈希表中的key。

    Python解法:

     1 class LRUCache:
     2 
     3     def __init__(self, capacity: int):
     4         self.dict = collections.OrderedDict()  # 有序字典
     5         self.remain = capacity
     6 
     7     def get(self, key: int) -> int:
     8         if key not in self.dict:    return -1
     9         v = self.dict.pop(key)  # 取出该值并删除它
    10         self.dict[key] = v  # 将其重新放回缓存,且处于顶部(由于是有序字典)
    11         return v
    12 
    13     def put(self, key: int, value: int) -> None:
    14         if key in self.dict:  # 如果key存在,则应先将其value删除再更新为新值
    15             self.dict.pop(key)  
    16         else:  # 如果key不存在
    17             # 缓存未满,放入新数值,容量减一
    18             if self.remain > 0: self.remain -= 1  
    19             # 缓存已满,放入新数值,popitem返回并删除字典中的最后一对键和值
    20             # Last=False,以队列方式弹出键值对,Last=True,以堆栈方式弹出键值对
    21             else:   self.dict.popitem(last = False)
    22         self.dict[key] = value  # 写入新数据
    23 
    24 # Your LRUCache object will be instantiated and called as such:
    25 # obj = LRUCache(capacity)
    26 # param_1 = obj.get(key)
    27 # obj.put(key,value)

    C++解法:

     1 struct DLinkedNode {
     2     int key, value;  // 双向链表的节点需要保存两个值
     3     DLinkedNode *prev;
     4     DLinkedNode *next;
     5     /*===========================================================
     6     构造函数后面的冒号起分割作用,是类给成员变量赋值的方法,初始化列表,
     7     更适用于成员变量是常量的const型
     8     ===========================================================*/
     9     DLinkedNode(): key(0),value(0),prev(NULL),next(NULL) {}  // 默认初始化
    10     DLinkedNode(int _key, int _value): key(_key),value(_value),prev(NULL),next(NULL) {}
    11 };
    12 
    13 class LRUCache {
    14 private:
    15     unordered_map<int, DLinkedNode*> cache;  // 创建哈希表
    16     DLinkedNode *head;
    17     DLinkedNode *tail;
    18     int size;  // 缓存元素数量
    19     int capacity;
    20 
    21 public:
    22     LRUCache(int _capacity):capacity(_capacity), size(0) {  // 构造函数
    23         head = new DLinkedNode();  // 哨兵头节点
    24         tail = new DLinkedNode();  // 哨兵尾结点
    25         head -> next = tail;
    26         tail -> prev = head;
    27     }
    28     void addToHead(DLinkedNode *node) {  // 元素添加至缓存头部
    29         node -> prev = head;
    30         node -> next = head -> next;
    31         head -> next -> prev = node;
    32         head -> next = node;
    33     }
    34     void removeNode(DLinkedNode *node) {  // 删除缓存元素
    35         node -> prev -> next = node -> next;
    36         node -> next -> prev = node -> prev;
    37     }
    38     void moveToHead(DLinkedNode *node) {  // 元素移至缓存头部
    39         removeNode(node);
    40         addToHead(node);
    41     }
    42     DLinkedNode *removeTail() {
    43         DLinkedNode *delNode = tail -> prev;  // 要被删除的链表尾节点
    44         removeNode(delNode);
    45         return delNode;  // 由于还需要从哈希表中删除,故返回该节点
    46     }
    47 
    48 
    49     int get(int key) {
    50         if(!cache.count(key))  // 如果要获取的元素不在缓存中
    51             return -1;
    52         else {
    53             DLinkedNode *getNode = cache[key];  // 通过哈希表定位该key的链表节点
    54             moveToHead(getNode);  // 将访问的节点移至缓存头部
    55             return getNode -> value;
    56         }
    57     }
    58     
    59     void put(int key, int value) {
    60         if(!cache.count(key)) {  // 如果要添加的元素不在缓存中
    61             DLinkedNode *newNode = new DLinkedNode(key, value);  // 创建一个新节点
    62             size++;
    63             if(size > capacity) {  // 如果超出容量
    64                 DLinkedNode *removedNode = removeTail();  // 从链表中删除尾节点
    65                 cache.erase(removedNode -> key);  // 从哈希表中删除对应的项
    66                 delete removedNode;  // 防止内存泄漏
    67                 size--;
    68             }
    69             cache[key] = newNode;  // 新节点添加进哈希表中
    70             addToHead(newNode);  // 添加至链表头部,由于同一key,故可通过哈希表定位该节点  
    71         }
    72         else {
    73             DLinkedNode *yetNode = cache[key];  // 定位到该节点
    74             yetNode -> value = value;  // 跟新value
    75             moveToHead(yetNode);  // 将访问的元素移至缓存头部
    76         }
    77     }
    78 };
    79 
    80 /**
    81  * Your LRUCache object will be instantiated and called as such:
    82  * LRUCache* obj = new LRUCache(capacity);
    83  * int param_1 = obj->get(key);
    84  * obj->put(key,value);
    85  */
  • 相关阅读:
    mysql 1
    mysql 创建库
    SQLServer2008数据库卸载图解
    同一个局域网中用Windows自己的远程桌面远程局域网中的其他PC
    WIN10怎么安装SQL server2000数据库
    怎样彻底卸载(删除)SQL server2000
    SQL Server 2000安装教程图解
    如何将网页保存为PDF文件
    【转】ArcGIS API for Silverlight/WPF 2.1学习笔记(四)
    【转】ArcGIS API for Silverlight/WPF 2.1学习笔记(五)
  • 原文地址:https://www.cnblogs.com/kongzimengzixiaozhuzi/p/13391817.html
Copyright © 2020-2023  润新知