leetcode | LRU Cache

LRU Cache : https://leetcode.com/problems/lru-cache/ Degree: hard

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 Cache 和使用的数据结构的文字内容来自 《如何用C++实现一个LRU Cache》 http://www.hawstein.com/posts/lru-cache-impl.html

解析

LRU :即最近最少使用缓存,换句话说就是最久没有使用的缓存,由于缓存的大小是有限的,只能容纳一定数量的数据,因此如何高效率的利用缓存成为一个问题。

什么是LRU Cache

LRU是Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法。 什么是Cache?狭义的Cache指的是位于CPU和主存间的快速RAM, 通常它不像系统主存那样使用DRAM技术,而使用昂贵但较快速的SRAM技术。 广义上的Cache指的是位于速度相差较大的两种硬件之间, 用于协调两者数据传输速度差异的结构。除了CPU与主存之间有Cache, 内存与硬盘之间也有Cache,乃至在硬盘与网络之间也有某种意义上的Cache── 称为Internet临时文件夹或网络内容缓存等。
Cache的容量有限,因此当Cache的容量用完后,而又有新的内容需要添加进来时, 就需要挑选并舍弃原有的部分内容,从而腾出空间来放新内容。LRU Cache 的替换原则就是将最近最少使用的内容替换掉。其实,LRU译成最久未使用会更形象, 因为该算法每次替换掉的就是一段时间内最久没有使用过的内容。

使用的数据结构

LRU的典型实现是hash map + doubly linked list, 双向链表用于存储数据结点,并且它是按照结点最近被使用的时间来存储的。 如果一个结点被访问了, 我们有理由相信它在接下来的一段时间被访问的概率要大于其它结点。于是, 我们把它放到双向链表的头部。当我们往双向链表里插入一个结点, 我们也有可能很快就会使用到它,同样把它插入到头部。 我们使用这种方式不断地调整着双向链表,链表尾部的结点自然也就是最近一段时间, 最久没有使用到的结点。那么,当我们的Cache满了, 需要替换掉的就是双向链表中最后的那个结点(不是尾结点,头尾结点不存储实际内容)。
注意:双向链表的头尾不存储内容,仅作为索引头尾指针的标记

哈希表的作用是什么呢?如果没有哈希表,我们要访问某个结点,就需要顺序地一个个找, 时间复杂度是O(n)。使用哈希表可以让我们在O(1)的时间找到想要访问的结点, 或者返回未找到。

实现流程

  1. 申请cache的内存空间,存放到free_entries_,hash-map,存储的关键字是 key,值是(key, value)的结构体node
  2. 对于Get(key)操作:从hash-map中寻找key
    • 如果找到,返回key所在的节点,并把该节点,从双向链表中拆下,然后安装到双向链表的头部;
    • 如果没找到,返回默认值
  3. 对于Set(key, value)操作:从hash-map中寻找key
    • 如果找到了 key,将key 对应的 node->value 替换成当前的value
    • 如果没有找到 key, 检查cache是否有空闲(free_entries_是否为空)
      • free_entries_ 非空,cache有空闲,从 free_entries_ 中取出一个新节点node,赋值(key, value),将该节点,安装到链表的头部,并将(key, node)压入hash-map
      • free_entries_ 为空,cache已满, 则将链表的最后一个节点node 拆下,并在hash-map中删除该项,然后对该节点重新赋值(key, value), 将该节点安装到链表的头部,并将(key,node)压入hash-map
  4. detachattach 操作
    • detach(node) : 将该节点从链表中移出
    • attach(node) : 将该节点链接到链表的头部

通用代码实现:

template <class K, class T>
struct Node {
    K key;
    T value;
    struct Node* next;
    struct Node* prev;
};
template <class K, class T>
class LRUCache{
private:
    Node<K, T>* entries_;
    vector<Node<K, T>*> free_entries_;
    hash_map<K, Node<K, T>* > cache;
    Node<K, T> *head_, *tail_;
private:
    void detach(Node<K, T>* node) {
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }
    void attach(Node<K, T>* node) {
        node->prev = head_;
        node->next = head_->next;
        head_->next->prev = node;
        head_->next = node;
    }
public:
    LRUCache(int capacity) {
        entries_ = new Node<K, T>[capacity];
        for (int i = 0; i < capacity; i++) {
            free_entries_.push_back(entries_+i);
        }
        head_ = new Node<K, T>;
        tail_ = new Node<K, T>;
        head_->prev = NULL;
        head_->next = tail_;
        tail_->prev = head_;
        tail_->next = NULL;
    }
    ~LRUCache() {
        delete entries_;
        delete head_;
        delete tail_;
    }

    T get(K key) {
        Node<K, T>* node = cache[key];
        if (node) {
            detach(node);
            attach(node);
            return node->value;
        } else {
            return T(); // 没有找到,返回T的默认值
        }
    }

    void set(K key, T value) {
        Node<K, T>* node = cache[key];
        if (node) {
            // 存在当前key
            detach(node);
            node->value = value;
            attach(node);
            return;
        } else {
            // 不存在,当前key
            if (!free_entries_.empty()) {
                // cache 有空闲时
                node = free_entries_.back();
                free_entries_.pop_back();
            } else {
                // cache 已满
                node =tail_->prev;
                detach(node);
                cache.erase(node->key);
            }
            node->key = key;
            node->value = value;
            cache[key] = node;
            attach(node);
        }
    }

    void printCache() {
        Node<K, T>* node = NULL;
        cout << "Print Cache" << endl;
        for (node = head_->next; node != tail_; node = node->next)
            cout << node->key << ", " << node->value << " => ";
        cout << endl;
    }
};

满足leetcode的c++代码实现

typedef struct Node {
    int key;
    int value;
    struct Node* next;
    struct Node* prev;
}Node;

class LRUCache{
private:
    Node* entries_;
    vector<Node*> free_entries_;
    unordered_map<int, Node*> cache;
    Node *head_, *tail_;
private:
    void detach(Node* node) {
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }
    void attach(Node* node) {
        node->prev = head_;
        node->next = head_->next;
        head_->next->prev = node;
        head_->next = node;
    }
public:
    LRUCache(int capacity) {
        entries_ = new Node[capacity];
        for (int i = 0; i < capacity; i++) {
            free_entries_.push_back(entries_+i);
        }
        head_ = new Node;
        tail_ = new Node;
        head_->prev = NULL;
        head_->next = tail_;
        tail_->prev = head_;
        tail_->next = NULL;
    }
    ~LRUCache() {
        delete entries_;
        delete head_;
        delete tail_;
    }

    int get(int key) {
        Node* node = cache[key];
        if (node) {
            detach(node);
            attach(node);
            return node->value;
        } else {
            return -1; // 没有找到,返回T的默认值
        }
    }

    void set(int key, int value) {
        Node* node = cache[key];
        if (node) {
            // 存在当前key
            detach(node);
            node->value = value;
            attach(node);
            return;
        } else {
            // 不存在,当前key
            if (!free_entries_.empty()) {
                // cache 有空闲时
                node = free_entries_.back();
                free_entries_.pop_back();
            } else {
                // cache 已满
                node =tail_->prev;
                detach(node);
                cache.erase(node->key);
            }
            node->key = key;
            node->value = value;
            cache[key] = node;
            attach(node);
        }
    }
};

参考资料:《如何用C++实现一个LRU Cache》http://www.hawstein.com/posts/lru-cache-impl.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值