LRU
概念:参考计算机系统课程
题目: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) 的平均时间复杂度运行
结构设计
要求均为O(1),第一选择就是hash+双链表
如果我们的设计是:unordered__map<int, int> um;//key找value O(1) list<pair<int, int>> li//头插头删尾插尾删O(1)
缺陷
:新增是O(1),更新是O(N),要调整li中key的位置到头部,更新顺序但是我们不知道数据在哪,只能遍历查找,这个就是O(N)
关键
:找到key的同时就找到其在li中的位置
使用迭代器:
typedef list<pair<int, int>>::iterator ListIter; unordered_map<int, ListIter> um; list<pair<int, int>> li;
只要找到key就能通过.find(),迭代器找到其在li的位置
代码:
class LRUCache { public: LRUCache(int capacity) :_capacity(capacity) {} int get(int key) { auto ret = um.find(key); if (ret != um.end()) { ListIter it = ret->second; //法1:erase 更新迭代器 li.splice(li.begin(), li, it);//注意list的insert和splice均不会使迭代器失效 return it->second; } else return -1; } void put(int key, int value) { //修改 auto ret = um.find(key); if (ret == um.end())//无元素或li中不存在 { if (_capacity == li.size()) { auto back = li.back(); um.erase(back.first); li.pop_back(); } li.push_front(make_pair(key, value)); um[key] = li.begin(); } else //存在 { ListIter it = ret->second; it->second = value; li.splice(li.begin(), li, it); } } private: typedef list<pair<int, int>>::iterator ListIter; unordered_map<int, ListIter> um; list<pair<int, int>> li; size_t _capacity; };
注意
:
- get逻辑找不到元素返回-1,找到需要将其置为li的首部操作:1.erase导致迭代器失效,需要自己控制,2.splice迭代器不会失效
- put逻辑分1. 元素存在 2.元素不存在(注意判满)