左神算法进阶班5_4设计可以变更的缓存结构(LRU)

【题目】

设计一种缓存结构,该结构在构造时确定大小,假设大小为K,并有两个功能:

set(key, value):将记录(key, value)插入该结构。

get(key):返回key对应的value值。

【要求】

1.set和get方法的时间复杂度为O(1)。

2.某个key的set或get操作一旦发生,认为这个key的记录成了最经常使用的。

3.当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。

【举例】

假设缓存结构的实例是cache,大小为3,并依次发生如下行为:

1.cache.set("A", 1)。最经常使用的记录为("A", 1)。

2.cache.set("B", 2)。最经常使用的记录为("B", 2),("A", 1)变为最不经常的。

3.cache.set("C", 3)。最经常使用的记录为("C", 3),("A", 1)还是最不经常的。

4.cache.get("A")。最经常使用的记录为("A", 1),("B", 2)变为最不经常的。

5.cache.set("D", 4)。大小超过了3,所以移除此时最不经常使用的记录("B", 2),

加入记录("D", 4),并且为最经常使用的记录,然后("C", 3)变为最不经常使用的

记录

【题解】

储存数据用hash_map,因为hash表的增删该查的复杂度都为O(1)

本来使用频率使用队列存储,使用的放在头,不使用的自动向后排,最不经常使用的在队尾

但每次从队列中取出正在使用的数据至队列头部的复杂度为O(N),不满足条件

所以只能使用双向链表来进行存储,

hash表中存放着每个数据节点的地址参数,故链表的中数据位置调整复杂度为O(1)

最经常使用的数据节点在链表的尾部,不经常使用的在链表的尾部,因为缓存数据时需要经常使用的,

而链表的尾部插入更加方便。

【代码】

  

  1 #pragma once
  2 #include <iostream>
  3 #include <hash_map>
  4 #include <deque>
  5 
  6 using namespace std;
  7 
  8 #define M 5//缓存空间的大小
  9 struct Node
 10 {
 11     int val;//数据存在链表中, 索引存在表中
 12     char c;
 13     Node* pre;
 14     Node* next;
 15     Node(char c, int a) :c(c), val(a), pre(nullptr), next(nullptr) {}
 16 };
 17 
 18 class Cash
 19 {
 20 public:
 21     void set(const char c, const int a);
 22     int get(const char c);
 23 
 24 private:
 25     void update(Node* p);//更新使用频率
 26     hash_map<char, Node*>map;
 27     Node* head = new Node(' ', -1);//指向链表的头
 28     Node* end = head;//指向链表的尾
 29 };
 30 
 31 void Cash::update(Node* p)
 32 {
 33     if (p == end)
 34         return;//p在链表尾部就不用移动了
 35     Node* q = p->pre;
 36     q->next = p->next;
 37     p->next->pre = q;
 38     end->next = p;
 39     p->pre = end;//更新p的使用率,并挪至链表尾部
 40     end = p;
 41 }
 42 
 43 void Cash::set(const char c, const int a)
 44 {
 45     if (map.find(c) == map.end())//不存在就存入
 46     {
 47         if (this->map.size() == M)//缓存空间已满
 48         {
 49             Node* p = this->head->next;
 50             this->head->next = p->next;//删除位于链表头部的最不常用的节点
 51             if (p->next == nullptr)//只有一个数据
 52                 end = head;
 53             else
 54                 p->next->pre = head;
 55             map.erase(p->c);//从表中删除,以留出空间
 56             delete p;
 57         }
 58         Node* p = new Node(c, a);//新插入的数据在链表尾
 59         this->end->next = p;
 60         p->pre = end;
 61         end = p;
 62         map[c] = p;//存入数据
 63     }
 64     else//存在,但要更新数的使用频率
 65     {
 66         Node* p = map[c];//得到在链表中的位置
 67         p->val = a;//更新数据值
 68         update(p);//更新使用频率
 69     }
 70 }
 71 
 72 
 73 int Cash::get(const char c)
 74 {
 75     if (map.find(c) == map.end())
 76     {
 77         cout << "the data is not existe!" << endl;
 78         return -1;
 79     }
 80     Node* p = map[c];
 81     update(p);//更新使用频率
 82     return p->val;
 83 }
 84 
 85 
 86 void Test()
 87 {
 88     Cash v;
 89     v.set('A', 1);
 90     v.set('B', 2);
 91     v.set('C', 3);
 92     v.set('D', 4);
 93     v.set('E', 5);
 94     v.set('F', 6);
 95     cout << v.get('A') << endl;
 96 
 97     cout << v.get('B') << endl;
 98     v.set('G', 7);
 99     cout << v.get('B') << endl;
100     cout << v.get('C') << endl;
101 
102 
103 }

 

转载于:https://www.cnblogs.com/zzw1024/p/11075341.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值