LeetCode 30天挑战 Day-24

LeetCode 30 days Challenge - Day 24

本系列将对LeetCode新推出的30天算法挑战进行总结记录,旨在记录学习成果、方便未来查阅,同时望为广大网友提供帮助。


LRU Cache

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

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.

The cache is initialized with a positive capacity.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

LRUCache cache = new LRUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
cache.get(2);       // returns -1 (not found)
cache.put(4, 4);    // evicts key 1
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4

Solution

题目要求分析:要求设计一个缓存机制,并实现访问get操作、put插入操作。

  1. get操作:当元素不存在,返回-1
  2. put操作:当占满空间后再次插入内容时,将最近最少使用的元素删去,替换之。

解法:

本题的朴素解法较简单,本文介绍一种O(1)访存的设计:双向链表(带头尾指针)+ 哈希结构(键值->链表结点)

先分析,该设计如何实现O(1)的访存要求:

  1. get操作:按照键值访问哈希表是O(1)的,找到哈希表对应的元素后,访问结点的val元素即可。

  2. put操作:

    1. 检测是否已存在该键值(unordered_map的find函数实现如下,是O(1)的)。

      _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
      	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
          find(const key_type& __k) const
          -> const_iterator
          {
            __hash_code __code = this->_M_hash_code(__k);
            std::size_t __n = _M_bucket_index(__k, __code);
            __node_type* __p = _M_find_node(__n, __k, __code);
            return __p ? const_iterator(__p) : end();
      
    2. 检测缓存是否已满是O(1)的。

    3. 在哈希表中删除(erase)是O(1)的。

    4. 在链表尾部删除结点、头部插入结点也是O(1)的。

综上,能保证是O(1)的访存。

接下来介绍实现中需要注意的几点:

  1. 删除LRU数据时依据是:通过双向链表尾结点的前一个结点,确定哈希表应该删除哪一表项。因此,链表结点除了记录数据之外,还要记录键值。m.erase(m.find(rear->pre->key)); // 哈希表的删除语句
  2. put操作需要进行的判断有三种情况,勿遗漏:
    1. 缓存已满,插入的键值不存在:删除LRU,在头部插入新键值对(结点)。
    2. 插入的键值已存在,此时不需考虑缓存是否已满:查找键值对应数据,更新为新值,并移动到头部。
    3. 插入的键值不存在,缓存未满的情况:直接在同步插入新键值对(结点)。

class LRUCache {
public:
    // 双向链表结点定义
    struct ListNode {
        int key;
        int val;
        ListNode* pre;
        ListNode* next;
        ListNode(int x, int y) : key(x), val(y), pre(NULL), next(NULL) {}
    };

    int cap;	// 容量
    ListNode* head = new ListNode(0, 0);	// 头指针
    ListNode* rear =  new ListNode(0, 0);	// 尾指针
    unordered_map<int, ListNode*> m;	// 哈希表

    // 构造函数
    LRUCache(int capacity) {
        cap = capacity;
        head->next = rear;
        rear->pre = head;
    }

    // 移动结点到头部
    void move2front(ListNode* node) {
        node->next->pre = node->pre;
        node->pre->next = node->next;
        node->next = head->next;
        node->pre = head;
        head->next->pre = node;
        head->next = node;
    }

     // 删除LRU
    void removeLRU() {
        ListNode* tmp = rear->pre;
        rear->pre = tmp->pre;
        tmp->pre->next = rear;
        delete tmp;
    }

     // 在头部插入新结点
    ListNode* insertNode(int key, int val) {
        ListNode* tmp = new ListNode(key, val);
        tmp->next = head->next;
        tmp->pre = head;
        head->next->pre = tmp;
        head->next = tmp;
        return head->next;
    }

    // get 访问操作
    int get(int key) {
        if (m.find(key) != m.end()) {
            move2front(m[key]);
            return m[key]->val;
        }
        else {
            return -1;
        }
    }

    // put 插入键值对
    void put(int key, int value) {
        if (m.find(key) == m.end() && m.size() == cap) {
            m.erase(m.find(rear->pre->key));
            removeLRU();
            m[key] = insertNode(key, value);
        }
        else if (m.find(key) != m.end()) {
            m[key]->val = value;
            move2front(m[key]);
        }
        else {
            m[key] = insertNode(key, value);
        }
    }
};

传送门:LRU Cache

2020/4 Karl

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值