一、概念
計算機的緩存容量有限,如果緩存滿了就要刪除一些內容,給新內容騰位置。但問題是,刪除哪些內容呢?我們肯定希望刪掉哪些沒什麼用的緩存,而把有用的數據繼續留在緩存裏,方便之後繼續使用。那麼,什麼樣的數據,我們判定爲「有用的」的數據呢?
LRU 緩存淘汰算法就是一種常用策略。LRU 的全稱是 Least Recently Used,即最近最少被使用,也就是說我們認爲最近使用過的數據應該是是「有用的」,很久都沒用過的數據應該是無用的,內存滿了就優先刪那些很久沒用過的數據。
二、算法描述
獲取數據 get 和 寫入數據 put 。
獲取數據 get(key) - 如果密鑰 (key) 存在於緩存中,則獲取密鑰的值(總是正數),否則返回 -1。
寫入數據 put(key, value) - 如果密鑰不存在,則寫入其數據值。當緩存容量達到上限時,它應該在寫入新數據之前刪除最近最少使用的數據值,從而爲新的數據值留出空間。
三、算法思想
要讓 put 和 get 方法的時間複雜度爲 O(1)O(1),我們可以總結出 cache 這個數據結構必要的條件:查找快,插入快,刪除快,有順序之分。
因爲顯然 cache 必須有順序之分,以區分最近使用的和久未使用的數據;而且我們要在 cache 中查找鍵是否已存在;如果容量滿了要刪除最後一個數據;每次訪問還要把數據插入到隊頭。
那麼,什麼數據結構同時符合上述條件呢?哈希表查找快,但是數據無固定順序;鏈表有順序之分,插入刪除快,但是查找慢。所以結合一下,形成一種新的數據結構:哈希鏈表。
LRU 緩存算法的核心數據結構就是哈希鏈表,雙向鏈表和哈希表的結合體。
四、代碼及具體思路
#include <list>
#include <iterator>
#include <unordered_map>
#include <map>
using namespace std;
class LRUCache {
public:
int cap = 0;
LRUCache(int capacity)
{
this->cap = capacity;
}
int get(int key)
{
auto iter = map.find(key);
//沒有在map中找到
if (iter == map.end())
return -1;
else
{
int value = iter->second->second;
cache.push_front(*(iter->second));
cache.erase(iter->second);
map[key] = cache.begin();
return value;
}
}
void put(int key, int value)
{
//先判斷key是否存在
auto iter = map.find(key);
//key不存在
if (iter == map.end())
{
//判斷緩存容量是否已滿
if (cache.size() == cap)
{
//容量滿需要釋放cache 和map
auto lst = cache.back();
map.erase(lst.first);
cache.pop_back();
}
//再插入
cache.push_front({ key,value });
map[key] = cache.begin();
}
//key已經存在
else
{
//先將緩存中原數據刪除
cache.erase(iter->second);
//再加入
cache.push_front({ key,value });
map[key] = cache.begin();
}
}
protected:
list<pair<int, int>> cache;
unordered_map <int, list<pair<int, int>>::iterator> map;
};
void main()
{
LRUCache cache (2);
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 該操作會使得密鑰 2 作廢
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 該操作會使得密鑰 1 作廢
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4
}
以上就是本篇文章的全部內容,如有不足,請多批評指正。