LRU Cache緩存替換算法

目錄

一、LRU 是什么?Cache是什么?

二、LRU Cache的實現

三、源碼


一、LRU 是什么?Cache是什么?

LRU 是 "Least Recently Used" 的縮寫,意思是“最近最少使用”。它是一種常用的 緩存(Cache)替換算法

緩存(Cache)就像一個臨時的“中轉站”或“快速工作臺”,它的作用是解決兩個速度差異很大的設備之間數據交換的“瓶頸”問題

例如

  • CPU 和內存之間:?我們電腦的 CPU 速度非常快,而主內存(通常用 DRAM 技術)相對較慢。為了不讓 CPU 總是“等”內存,就在它們之間加了一個高速緩存(通常用更快的 SRAM 技術)。CPU 會優先從這個高速緩存里找數據,大大提升了效率。
  • 內存和硬盤之間:硬盤速度比內存慢得多,操作系統會在內存里開辟一塊區域作為硬盤的緩存,存放最近讀寫過的數據。
  • 硬盤和網絡之間:?當你瀏覽網頁時,瀏覽器會把看過的圖片、網頁文件等暫時保存在硬盤上的?“Internet 臨時文件夾”或“網絡緩存”?里。下次再訪問同一個網站,瀏覽器就可以直接從本地緩存加載,而不必每次都從慢速的網絡重新下載,讓網頁打開更快。

總之,LRU 是管理緩存空間的一種策略(當緩存滿了,優先淘汰最久沒被用過的數據)。而緩存本身,則是解決不同速度設備間協作效率問題的關鍵設計,存在于計算機系統的多個層級。Cache的容量有限,因此當Cache的容量用完后,而又有新的內容需要添加進來時, 就需要挑選 并舍棄原有的部分內容,從而騰出空間來放新內容。LRU Cache 的替換原則就是將最近最少使用 的內容替換掉。其實,LRU譯成最久未使用會更形象, 因為該算法每次替換掉的就是一段時間內 最久沒有使用過的內容。

二、LRU Cache的實現

實現LRU Cache的方法和思路很多,但是要保持高效實現O(1)的put和get,且其涉及到兩個核心問題:快速訪問數據維護數據的使用順序,那么使用雙向鏈表和 哈希表的搭配是最高效和經典的。使用雙向鏈表是因為雙向鏈表可以實現任意位置O(1)的插入和 刪除(維護數據使用順序),使用哈希表是因為哈希表的增刪查改也是O(1)和快速訪問。

算法的思想:緩存容量有限,當緩存滿了時,優先淘汰最久未被訪問的數據,保留最近使用過的數據。?

核心的數據結構

  1. 哈希表:用于?O(1) 時間?快速查詢、插入、刪除鍵值對。
  2. 雙向鏈表:用于維護數據的訪問順序
    • 最近訪問的或者新插入的放在鏈表頭部
    • 最久未訪問的放在鏈表尾部
    • 當緩存滿時,直接刪除鏈表尾部的數據。

?接下來借助一個 OJ 題來幫助實現 LRU Cache 算法。題目描述、示例提示如下

?

題目分析:

題目中要求函數 get 和 put 必須以 O(1) 的平均時間復雜度運行。

題目要求我們實現一個滿足 LRU (最近最少使用) 緩存 約束的數據結構。使用雙向鏈表和哈希表的搭配是最高效和經典的。

  //hash做到查找更新/插入是O(1)unordered_map<int, LtIter> _hashmap;//LRU 默認鏈表尾部的是最久未被使用的list<pair<int, int>> _LRUList;

?但是只有這些是遠遠不夠的,更新的時候其實復雜度是O(N),更新的情況就是調用put先在哈希表里面查找到key是已存在的,那然后我們要修改,哈希表里面我找到這個就可以直接修改。 但是,在list里也要修改,因為我們替換的時候找最久未被使用的那個值就是要從list里面找。 但是要修改list的話我們知不知道當前要修改的這個元素是list里面的哪一個元素? 是不知道的,所以還得遍歷list去找。找到之后更新一下,然后把它移到頭部去,更新順序。

所以接下來我們就需要一個設計:
還是list和unordered_map搭配。 list里面呢還是存key-value的鍵值對pair。然后哈希表里面key還是要存的,但是不再像上面寫的那樣直接存key對應的數據value,而是存這個key對應的元素在list里面的對應的迭代器。(那這樣真正的數據就只存在list里面)

那這樣的話如果更新的話,首先我們在哈希表里面找到key,然后通過它里面存的該元素在list中的迭代器,就可以直接修改list里面存放的數據。

private:typedef list<pair<int,int>>::iterator LtIter;//hash做到查找更新/插入是O(1)unordered_map<int, LtIter> _hashmap;//LRU 默認鏈表尾部的是最久未被使用的list<pair<int, int>> _LRUList;size_t _capacity;//緩存的容量控制,當緩存大小超過此值時,需要淘汰最久未使用的元素

構造函數:

class LRUCache {
public:LRUCache(int capacity):_capacity(capacity){}

get() 方法實現:

int get(int key) {auto ret=_hashmap.find(key);if(ret!=_hashmap.end()){LtIter it=ret->second;//獲取哈希表中存儲的鏈表迭代器//將it對應的結點轉移到鏈表頭部_LRUList.splice(_LRUList.begin(),_LRUList,it);//操作是 O(1) 時間復雜度的,因為它只修改指針而不需要復制數據return it->second->second;}return -1;
}

上面的splice接口功能如下:它可以把一個鏈表的一部分轉移到另一個鏈表(當然也可以是同一個鏈表直接進行轉移) 所以我們就可以直接調用splice將這個結點轉移到list的頭部。?

put()接口的實現:

put的話呢無非就兩種操作 如果關鍵字 key 已經存在,則變更其數據值 value ;如果不存在,則向緩存中插入該組 key-value 。 當然插入的時候需要判斷: 如果插入操作導致關鍵字數量超過 capacity ,則應該 逐出 最久未使用的關鍵字,然后插入新值。 另外不論是插入還是更新,都應該把插入或更新的值放到鏈表頭部。

void put(int key, int value) {auto ret=_hashmap.find(key);//如果找到,更新值if(ret!=_hashmap.end()){LtIter it=ret->second;it->second=value;//將it對應的結點轉移到鏈表頭部_LRUList.splice(_LRUList.begin(),_LRUList,it);}else//找不到,插入{//如果滿了需要先刪除最久未使用的值if(_capacity==_hashmap.size()){pair<int,int> back=_LRUList.back();_hashmap.erase(back.first);_LRUList.pop_back();}//插入_LRUList.push_front(make_pair(key,value));_hashmap[key]=_LRUList.begin();}
}

三、源碼

class LRUCache {
public:LRUCache(int capacity):_capacity(capacity){}int get(int key) {auto ret=_hashmap.find(key);if(ret!=_hashmap.end()){LtIter it=ret->second;//將it對應的結點轉移到鏈表頭部_LRUList.splice(_LRUList.begin(),_LRUList,it);return it->second;}return -1;}void put(int key, int value) {auto ret=_hashmap.find(key);//如果找到,更新值if(ret!=_hashmap.end()){LtIter it=ret->second;it->second=value;//將it對應的結點轉移到鏈表頭部_LRUList.splice(_LRUList.begin(),_LRUList,it);}else//找不到,插入{//如果滿了需要先刪除最久未使用的值if(_capacity==_hashmap.size()){pair<int,int> back=_LRUList.back();_hashmap.erase(back.first);_LRUList.pop_back();}//插入_LRUList.push_front(make_pair(key,value));_hashmap[key]=_LRUList.begin();}}
private:typedef list<pair<int,int>>::iterator LtIter;//hash做到查找更新/插入是O(1)unordered_map<int, LtIter> _hashmap;//LRU 默認鏈表尾部的是最久未被使用的list<pair<int, int>> _LRUList;size_t _capacity;
};

感謝閱讀!?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/92371.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/92371.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/92371.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

自定義視圖:圖形與圖像的處理(二):繪圖

除了使用已有的圖片之外&#xff0c;Android應用還常常需要在運行時動態地生成圖片&#xff0c;比如一個手機游戲&#xff0c;游戲界面看上去豐富多彩&#xff0c;而且可以隨著用戶動作而動態改變&#xff0c;這就需要借助于Android的繪圖支持了。1. Android繪圖基礎:Canvas、P…

微服務、服務網格、Nacos架構與原理

Nacos架構與原理 -服務網格生態-阿里云開發者社區 ------ 該文章用于學習參考,如有侵權,請直接聯系下架 服務網格的核心職責:治理“服務通信” 包括但不限于: 功能 舉例說明 負載均衡 動態選擇服務實例 熔斷、重試 某個服務失敗時自動切換、重試 流量路由 灰度發布、藍綠…

STM32——啟動過程淺析

總&#xff1a;STM32——學習總綱 參考文件&#xff1a; STM32 MAP文件淺析-V1.1 STM32 啟動文件淺析_V1.2 Cortex-M3權威指南(中文)、ARM Cotrex-M3權威指南(英文).zip 一、Map文件解析 1.1 MDK編譯過程文件 在編譯中&#xff0c;會生成11種編譯過程文件&#xff0c;可…

區塊鏈簡介

一、區塊鏈簡介 狹義上的定義&#xff1a; 區塊鏈是一種鏈式數據結構&#xff0c;通過按時間順序將數據塊逐一連接形成。這種結構通過密碼學確保了數據的不可篡改性和不可偽造性&#xff0c;形成了一種分布式賬本技術。 廣義上的定義&#xff1a; 區塊鏈技術不僅僅是一種數據…

NestJS中@Injectable裝飾器

一、基礎定義與核心作用 1.1 什么是Injectable&#xff1f; Injectable() 是 NestJS 依賴注入&#xff08;Dependency Injection, DI&#xff09;系統的核心裝飾器&#xff0c;用于將類標記為可注入的提供者&#xff08;Provider&#xff09;。它告知 NestJS 的 IoC&#xff08…

【機器學習深度學習】大模型應用落地:微調與RAG的角色與實踐

目錄 前言 一、微調與RAG&#xff1a;大模型應用落地的兩大支柱 1. 微調&#xff08;Fine-tuning&#xff09; 2. RAG&#xff08;Retrieval-Augmented Generation&#xff09; 二、微調可以做什么&#xff1f; 1. 模型自我認知調整 2. 對話風格優化 3. 提升問題理解能…

List、ArrayList 與順序表

目錄 一、List 介紹 二、線性表 三、自己實現 ArrayList 3.1 顯示元素 3.2 增 3.2.1 默認在數組后面新增元素 3.2.2 在指定位置中新增元素 3.3 查 3.4 取值 3.5 改 3.5.1 把 pos 位置的元素修改成 value 3.5.2 刪除某個元素 3.5.3 清空 四、認識 ArrayList 4.0 說…

Baumer工業相機堡盟工業相機如何通過YoloV8深度學習模型實現各類垃圾的分類檢測識別(C#代碼UI界面版)

Baumer工業相機堡盟工業相機如何通過YoloV8深度學習模型實現各類垃圾的分類檢測識別&#xff08;C#代碼UI界面版&#xff09;工業相機使用YoloV8模型實現各類垃圾的分類檢測識別工業相機通過YoloV8模型實現各類垃圾的分類檢測識別的技術背景在相機SDK中獲取圖像轉換圖像的代碼分…

EasyExcel高效工具類:簡化Excel導入導出,支持多Sheet與枚舉轉換

文章目錄前言一、依賴坐標二、工具類&#xff1a;ExcelUtil三、測試1.實體類2.前置操作3.單Sheet導出4.單Sheet導入5.多Sheet導出6.多Sheet導入7.完整代碼四、擴展&#xff1a;自定義注解實現枚舉類型轉換1.枚舉接口2.枚舉類3.注解4.轉換類5.使用示例6.測試總結前言 在現代應用…

技術速遞|GitHub Copilot for Eclipse 邁出重要一步

我們非常高興地宣布&#xff1a;2025 年 7 月 22 日&#xff0c;GitHub Copilot for Eclipse 又邁出了重要一步&#xff0c;Eclipse 變得更智能、更快捷&#xff0c;而且與 Eclipse 的集成也更無縫了&#xff01;這是繼新功能上線以來&#xff0c;又一次質的提升。 &#x1f…

Coze Loop:開源智能體自動化流程編排平臺原理與實踐

項目簡介 Coze Loop 是 Coze 團隊開源的智能體自動化流程編排平臺。它以“Loop”為核心概念,支持開發者通過低代碼/可視化方式,將多種 AI Agent、插件、API、數據流等靈活編排為自動化工作流,實現復雜的智能體協作、任務自動化和多模態數據處理。Coze Loop 適用于企業自動化…

[GESP202309 四級] 2023年9月GESP C++四級上機題題解,附帶講解視頻!

本文為2023年9月GESP C四級的上機題目的詳細題解&#xff01;覺得寫的不錯或者有幫助可以點個贊啦。 目錄 題目一講解視頻: 題目二講解視頻: 題目一:進制轉換 解題思路: 代碼(C): 題目二:變長編碼 解題思路: 代碼(C): 題目一講解視頻: 2023年9月GESP C四級上機題一題目…

【AI編程工具IDE/CLI/插件專欄】-國外IDE與Cursor能力對比

AI編程專欄(二) - Cursor 深度使用指南 Cursor 深度使用指南(二) - 新能力使用教程 從Trae 2.0與CodeBuddy IDE發布&#xff0c;談大廠布局IDE 如何選擇AI IDE&#xff1f;對比Cursor分析功能差異 AI編程工具IDE/CLI/插件專欄-熱門AI編程CLI初識與IDE對 前面文章介紹過了國…

word2vector細致分解(CBOW, SKIP_GRAM, 層次soft Max, 負采樣)

1 前世今生&#xff1a;NGRAM NGRAM&#xff1a;將詞當成一個離散的單元&#xff08;因此存在一定的局限性&#xff0c;沒有考慮到詞與詞之間的關系&#xff09; neural network language model&#xff1a;只能處理定長序列&#xff0c;訓練慢。使用RNN之后有所改善 2 兩種訓…

Elasticsearch向量庫

在Elasticsearch&#xff08;ES&#xff09;最新版本&#xff08;目前8.x系列&#xff09;中&#xff0c;無需額外的“embedding插件”&#xff0c;因為ES從7.14版本開始就原生支持向量數據類型&#xff08;dense_vector&#xff09; 和向量搜索能力&#xff0c;可直接作為向量…

嵌入式學習的第四十四天-ARM

一、ARM內核基礎知識1.ALU算術邏輯單元&#xff1b;完成運算的電路2.通用寄存器&#xff1a;R0~R15R13&#xff08;SP&#xff09;&#xff1a;棧指針寄存器&#xff1a;指向棧的指針&#xff08;指向正確的位置&#xff09;&#xff0c;為了保護現場 R14&#xff08;LR…

QML開發:QML中的基本元素

文章目錄一、概述二、常用基本元素2.1 基礎視覺元素&#xff08;常用于布局和顯示&#xff09;2.1.1 元素 Item 的介紹和使用2.1.2 元素 Rectangle 的介紹和使用2.1.3 元素 Image 的介紹和使用2.1.4 元素 Text 的介紹和使用2.2 交互元素&#xff08;用于接收用戶操作&#xff0…

Spring AI 項目實戰(二十二):Spring Boot + AI +DeepSeek實現智能合同數據問答助手?(附完整源碼)

系列文章 序號 文章名稱 1 Spring AI 項目實戰(一):Spring AI 核心模塊入門 2 Spring AI 項目實戰(二):Spring Boot + AI + DeepSeek 深度實戰(附完整源碼) 3 Spring AI 項目實戰(三):Spring Boot + AI + DeepSeek 打造智能客服系統(附完整源碼) 4

從 0 到 1 創建 InfluxDB 3 表:標簽、字段、命名規范一篇講透

前言 在使用 InfluxDB 3 存儲時序數據時,表的設計堪比蓋房子打地基,地基打歪,數據“塌方”指日可待。InfluxDB 雖然不是傳統意義上的關系型數據庫,但它有自己的一套“審美”:標簽(Tags)和字段(Fields)是它的雙核心,誰先誰后,關系重大,順序寫錯,查詢性能立馬打折。…

[sqlserver] 分析SQL Server中執行效率較低的SQL語句

查詢性能分析較低的SQL語句 -- 查詢性能分析 SELECT TOP 50qs.creation_time AS [編譯時間],qs.last_execution_time AS [最后執行時間],qs.execution_count AS [執行次數],qs.total_worker_time/1000 AS [CPU總時間(ms)],qs.total_elapsed_time/1000 AS [總耗時(ms)],(qs.tota…