当前位置:   article > 正文

LRU缓存 数据结构设计(C++)_请你设计并实现一个满足lru(最近最少使用)缓存约束的数据结构 实现lrucache类

请你设计并实现一个满足lru(最近最少使用)缓存约束的数据结构 实现lrucache类

做LeetCode第146题LRU缓存,觉得收获不小,特此记录。

请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类:

  • LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存。
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该逐出最久未使用的关键字。

函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

关于LRU的规则,学过操作系统的肯定都知道,此处不再赘述。

题目关键在于 get() 和 put() 必须O(1),这就使得我们选择数据结构的时候需要有些考究。

首先我们回顾一下本科学过的数据结构知识,存储这样的多个key-value对肯定是使用线性表存储,而线性表又分为顺序表(数组)和链表,由于题目中涉及到了插入和删除(逐出),且有O(1)的条件限制,所以肯定是使用链表来存储。

确定了基本数据结构,我们再来想想如何使用或改造使其满足题目需要。

首先考虑 get(),我们需要做到如果关键字key在缓存中,就能查找到关键字的值,且有O(1)的条件限制,那自然是使用哈希表哈希表的键就是LRU缓存中的key自然不用说,关键在于 哈希表的值是什么?直观想法那就是LRU的value呗,其实不然,因为我们其实不仅要通过key查到value,还有一个隐含的操作,即需要把我们刚刚查过的这个LRU缓存节点置于最高优先级的位置上(LRU的规则),所以该哈希表的value应该是一个链表节点,我们通过key查到这个链表节点以后,就可以对其进行操作以改变其优先级,同时我们也可以直接通过node->value来取得key对应的value值,一举两得。

那到底怎么体现优先级,怎么做到关键字的插入和逐出呢,其实很简单,在链表头部的表示优先级最高(即最近使用),在链表尾部的表示优先级最低(即最久未使用),这样一来 put() 的编写就容易了,当来一个新关键字的时候,就把其节点插入链表头,同时更新哈希表。每当链表中的节点个数超过LRU的容量时,我们就删除掉链表尾的的元素,同时更新哈希表。

现在我们还有一个问题需要解决,就是使用普通的单链表,是否能满足put()和get()均为O(1)的题目要求。根据以上的分析,我们对链表会有以下操作:

  1. 把一个新节点插入链表头。当我们put()一个新关键字,其不存在于缓存中,且缓存没有满时,就把这个新的关键字节点插入链表头。
  2. 把一个链表中的节点移动到链表头。当我们get()一个关键字,其存在于缓存中,我们就需要把这个节点从当前位置插入到链表头部。
  3. 删除链表尾的节点。当我们put()一个新关键字,其不存在于缓存中,我们需要插入新节点,但此时缓存满了,我们需要把最近最少使用的节点从链表中剔除掉,也就是删除链表尾的节点。

根据以上对链表的要求,可以确定只有双链表能满足要求,单链表、单循环链表、带尾指针的单循环链表都无法在O(1)的时间下做到以上三个操作,大家可以模拟一下。

确定好数据结构,代码就很好写了,只需按需实现LRU功能即可:

struct doubleLinkNode {
    int key, value;
    doubleLinkNode *pre, *next;
};

class LRUCache {
private:
    doubleLinkNode *head, *rear;
    int size, capacity;
    unordered_map<int, doubleLinkNode*> map;

    void insert(doubleLinkNode* node) {
        // 把一个非链表上的节点插入到表头
        node->pre = head;
        node->next = head->next;
        head->next->pre = node;
        head->next = node;
    }

    void insertHead(doubleLinkNode* node) {
        // 把一个链表上的节点插入到表头
        node->pre->next = node->next;
        node->next->pre = node->pre;
        insert(node);
    }

    int deleteRear() {
        doubleLinkNode *node = rear->pre;
        node->pre->next = node->next;
        node->next->pre = node->pre;
        int key = node->key;
        delete node;
        return key;
    }

public:
    LRUCache(int cap): size(0), capacity(cap) {
        head = new doubleLinkNode();
        rear = new doubleLinkNode();
        head->next = rear;
        head->pre = nullptr;
        rear->next = nullptr;
        rear->pre = head;
    }
    
    int get(int key) {
        if(map.count(key)) {
            insertHead(map[key]);
            return map[key]->value;
        }
        return -1;
    }
    
    void put(int key, int value) {
        if(map.count(key)) {
            doubleLinkNode *node = map[key];
            if(node->value != value) {
                node->value = value;
            }
            insertHead(node);
        } else {
            doubleLinkNode *node = new doubleLinkNode;
            node->key = key;
            node->value = value;
            insert(node);
            ++size;
            if(size > capacity) {
                int nodeKey = deleteRear();
                map.erase(nodeKey);
                --size;
            }
            map[key] = node;
        }
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/491676
推荐阅读
相关标签
  

闽ICP备14008679号