【题目】
设计一种缓存结构,该结构在构造时确定大小,假设大小为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 #include3 #include 4 #include 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 }