> 作者:დ旧言~
> 座右铭:松树千年终是朽,槿花一日自为荣。> 目标:了解什么是LRU Cache,并能简单的模拟实现。
> 毒鸡汤:有些事情,总是不明白,所以我不会坚持。早安!
> 专栏选自:数据结构
> 望小伙伴们点赞👍收藏✨加关注哟💕💕
一、什么是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 Cache的实现
概念:
实现LRU Cache的方法和思路很多,但是要保持高效实现O(1)的put和get,那么使用 双向链表和 哈希表
的搭配是最高效和经典的。使用双向链表是因为双向链表可以实现任意位置O(1)的插入和删除,使用哈希表是因为哈希表的增删查改也是O(1)。
说明:
- 保证查找和更新 key值对应的value的时间复杂度为O(1), 也就是保证了get,但是不能保证LRU,因为无法从哈希表中知道知道key值在双向链表中的位置,因此无法用O(1)的时间复杂度来将key值放到链表的头部。
- 我们可以让哈希表中存储key值和key值在双向链表中的位置,因此可以让哈希表中存储key值和key值在双向链表中的位置——迭代器,双向链表存储的是键值对pair<key, value>,这样就可以保证 LRU Cache了。
代码实现:
class LRUCache {
public:LRUCache(int capacity):_capacity(capacity){}int get(int key) {auto ret = _hashMap.find(key);if(ret != _hashMap.end()){//更新key的位置// 1. erase + push_front 更新迭代器, 原迭代器失效// 2. splice 将节点转移到头部ListIterator it = ret->second;_LRUList.splice(_LRUList.begin(), _LRUList, it);return it->second;}else return -1;}void put(int key, int value) {// 判断是 1.新增 还是 2. 更新auto ret = _hashMap.find(key);if(ret == _hashMap.end()) // 新增数据{// 容量已满if(_capacity == _hashMap.size()){pair<int, int> LRUData = _LRUList.back();_hashMap.erase(LRUData.first);_LRUList.pop_back();}_LRUList.push_front(make_pair(key, value));_hashMap[key] = _LRUList.begin();}else // 更新{// 更新key对应的值ListIterator it = ret->second;it->second = value;//更新key对应值的位置——splice 将节点转移到头部_LRUList.splice(_LRUList.begin(), _LRUList, it);}}
private:typedef list<pair<int, int>>::iterator ListIterator;//哈希表——查找和更新的时间复杂度为O(1),哈希表中存储的是//key和key在_LRUList中的位置(迭代器)unordered_map<int, ListIterator> _hashMap;list<pair<int,int>> _LRUList;size_t _capacity; //容量
};
三、LRU Cache的OJ
LRU缓存. - 力扣(LeetCode)
class LRUCache {
public:LRUCache(int capacity):_capacity(capacity){}int get(int key) {auto ret = _hashMap.find(key);if(ret != _hashMap.end()){//更新key的位置// 1. erase + push_front 更新迭代器, 原迭代器失效// 2. splice 将节点转移到头部ListIterator it = ret->second;_LRUList.splice(_LRUList.begin(), _LRUList, it);return it->second;}else return -1;}void put(int key, int value) {// 判断是 1.新增 还是 2. 更新auto ret = _hashMap.find(key);if(ret == _hashMap.end()) // 新增数据{// 容量已满if(_capacity == _hashMap.size()){pair<int, int> LRUData = _LRUList.back();_hashMap.erase(LRUData.first);_LRUList.pop_back();}_LRUList.push_front(make_pair(key, value));_hashMap[key] = _LRUList.begin();}else // 更新{// 更新key对应的值ListIterator it = ret->second;it->second = value;//更新key对应值的位置——splice 将节点转移到头部_LRUList.splice(_LRUList.begin(), _LRUList, it);}}
private:typedef list<pair<int, int>>::iterator ListIterator;//哈希表——查找和更新的时间复杂度为O(1),哈希表中存储的是//key和key在_LRUList中的位置(迭代器)unordered_map<int, ListIterator> _hashMap;list<pair<int,int>> _LRUList;size_t _capacity; //容量
};
四、结束语
今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。