1.概念介紹
假設緩存的大小固定,初始狀態爲空。每發生一次讀內存操作,首先查找待讀取的數據是否
存在於緩存中,如果存在則緩存命中,返回數據,並將緩存數據放到緩存區頭部位置;否則緩存未命中,返回提示信息。
向緩存添加數據時,如果緩存已滿,則需要刪除訪問時間最早的數據,這種更新緩存的方法就叫做LRU(Least Recently Used)。
2. 實際實現LRUCache類
基本要求如下
- LRUCache(int capacity) 用一個正整數表示的容量大小初始化緩存空間。
- int get(int key) 如果對應的鍵存在於緩存中,即命中返回鍵對應的值,未命中返回-1。
- void put(int key, int value) 如果已存在更新其內容。否則將鍵值對添加到緩存中。 如果超過了最大容量,淡出最近最少使用的鍵值對。
同時要求get和put操作保證平均時間複雜度爲O(1)。
案例介紹
-
示例輸入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]] -
輸出
[null, null, null, 1, null, -1, null, -1, 3, 4] -
過程解釋
LRUCache lRUCache = new LRUCache(2);
LRUCache.put(1, 1); // 緩存內容變爲 {1=1}
LRUCache.put(2, 2); // 緩存內容變爲 {1=1, 2=2}
LRUCache.get(1); // 返回鍵爲1的緩存內容對應的值
LRUCache.put(3, 3); // LRU鍵爲2, 添加到緩存中前先移除鍵爲2的緩存內容然後添加新內容, 緩存內容變爲 {1=1, 3=3}
LRUCache.get(2); // 緩存中不存在就返回-1
LRUCache.put(4, 4); // LRU鍵爲1, 先移除鍵爲1的緩存內容然後添加新內容, 緩存內容變爲 {4=4, 3=3}
LRUCache.get(1); // 緩存中不存在就返回-1
LRUCache.get(3); // 返回3
LRUCache.get(4); // 返回4
3.C++參考實現
class LRUCache {
public:
struct ListNode {
int key;
int val;
ListNode *prev;
ListNode *next;
ListNode(): key(0), val(0), prev(nullptr), next(nullptr) {}
ListNode(int _key, int _val): key(_key), val(_val),
prev(nullptr), next(nullptr) {}
};
public:
LRUCache(const int capacity): cap(capacity) {
head = new ListNode(-1, 0);
tail = new ListNode(-1, 0);
head->next = tail;
tail->prev = head;
}
~LRUCache(void) {
if (cap>0)
{
ListNode *curNode = head->next;
// 釋放鏈表中除首尾指示結點外所有結點佔用的資源
while (curNode != tail)
{
ListNode *tmpNode = curNode;
curNode = curNode->next;
delete tmpNode;
tmpNode = nullptr;
}
// 清空哈希表
mp.clear();
// 重置緩存容量
cap = 0;
}
// 釋放首尾指針資源並將其設置爲空指針
delete head;
delete tail;
head = nullptr;
tail = nullptr;
}
int get(int key) {
if (mp.find(key)==mp.end())
return -1;
ListNode *curNode = mp[key];
// 如果已存在於當前緩存中,將其提前到鏈表首部位置
moveToHead(curNode);
return curNode->val;
}
void put(int key, int val) {
if (mp.find(key)!=mp.end())
{
ListNode *curNode = mp[key];
// 如果已存在於當前緩存中,將其提前到鏈表首部位置
moveToHead(curNode);
curNode->val = val;
} else {
if (cap>0 && mp.size()==cap)
{
// 刪除最後一個結點,因爲時間最久沒被訪問
deleteNode(tail->prev);
}
// 在鏈表頭部添加新結點
ListNode *tmpNode = new ListNode(key, val);
mp[key] = tmpNode;
addNode(tmpNode);
}
}
private:
void moveToHead(ListNode *pnode)
{
// 斷開當前結點與前後結點的連接
curNode->prev->next = curNode->next;
curNode->next->prev = curNode->prev;
// 將結點連到鏈表頭部位置
curNode->next = head->next;
head->next->prev = curNode;
head->next = curNode;
curNode->prev = head;
}
void deleteNode(ListNode *pnode)
{
pnode->prev->next = pnode->next;
pnode->next->prev = pnode->prev;
delete pnode;
pnode = nullptr;
}
void addNode(ListNode *pnode)
{
pnode->next = head->next;
head->next->prev = pnode;
head->next = pnode;
pnode->prev = head;
}
private:
int cap;
std::unordered_map<int, ListNode*> mp;
ListNode *head;
ListNode *tail;
};
本文作者 :phillee
發表日期 :2022年03月07日
本文鏈接 :https://www.cnblogs.com/phillee/p/15975167.html
版權聲明 :自由轉載-非商用-非衍生-保持署名(創意共享3.0許可協議/CC BY-NC-SA 3.0)。轉載請註明出處!
限於本人水平,如果文章和代碼有表述不當之處,還請不吝賜教。