手撕LFU緩存

手撕LRU緩存_右大臣的博客-CSDN博客

是LRU的升級,多了一個訪問次數的維度

實現?LFUCache?類:

  • LFUCache(int capacity)?- 用數據結構的容量?capacity?初始化對象
  • int get(int key)?- 如果鍵?key?存在于緩存中,則獲取鍵的值,否則返回?-1?。
  • void put(int key, int value)?- 如果鍵?key?已存在,則變更其值;如果鍵不存在,請插入鍵值對。當緩存達到其容量?capacity?時,則應該在插入新項之前,移除最不經常使用的項。在此問題中,當存在平局(即兩個或更多個鍵具有相同使用頻率)時,應該去除?最近最久未使用?的鍵。

為了確定最不常使用的鍵,可以為緩存中的每個鍵維護一個?使用計數器?。使用計數最小的鍵是最久未使用的鍵。

當一個鍵首次插入到緩存中時,它的使用計數器被設置為?1?(由于 put 操作)。對緩存中的鍵執行?get?或?put?操作,使用計數器的值將會遞增。

函數?get?和?put?必須以?O(1)?的平均時間復雜度運行。

LRU的實現是一個哈希表加上一個雙鏈表
而LFU則要復雜多了,需要用兩個哈希表再加上N個雙鏈表才能實現

LFU需要為每一個頻率構造一個雙向鏈表

460. LFU 緩存 - 力扣(LeetCode)? 一個邏輯講解

總的來說就是下圖這樣:

?所以針對LRU的模仿寫法,我就直接用STL的list去做

因為LFU多了頻率,所以需要重構結點,,就不像LRU那個一樣用pair對組了

//因為多了頻率,所以重構結點,,就不像LRU那個一樣用pair對組了
struct Node
{int key;int value;int frequency;Node(int k,int v,int f=1):key(k),value(v),frequency(f){}
};
class LFUCache {
private:int cap;int minfre;//最小的頻率unordered_map<int,list<Node>::iterator>mpkey;//用記錄key -val 緩存unordered_map<int,list<Node>>mpfre;//記錄頻率訪問次數的public:LFUCache(int capacity=10) {cap = capacity;minfre = 1;mpkey.clear();mpfre.clear();}int get(int key) {//沒有這個結點if(mpkey.count(key)==0){return -1;}//有結點 ,保存結點信息auto it=mpkey[key];//找到結點位置int fre1=it->frequency;//找到對應的頻率int val=it->value;//開始操作記錄頻率的表了mpfre[fre1].erase(it);//刪除這個頻率鏈表上的結點if(mpfre[fre1].size()==0){//刪完如果他空了//就把這個鏈表給刪了mpfre.erase(fre1);if(fre1==minfre){//如果這個頻率他剛好是最小的minfre++;} }   //把這個結點加到高頻率的鏈表頭mpfre[fre1+1].push_front(Node(key,val,fre1+1));//更新結點緩存表的指向mpkey[key]=mpfre[fre1+1].begin();return mpkey[key]->value;}void put(int key, int value) {if(get(key)!=-1){//有這個結點//get已經幫忙更新過了,只需要把值改了就行mpkey[key]->value=value;}else{//沒有這個結點,需要新添加//看結點緩存滿不滿if(mpkey.size()==cap){//根據LRU算法,找到最小頻率的尾巴的結點,刪除auto it=mpfre[minfre].back();mpkey.erase(it.key);mpfre[minfre].pop_back();//如果刪完 最小頻率這個鏈表他空了if(mpfre[minfre].size()==0){mpfre.erase(minfre);}}//添加 新添加的頻率置為1int freque=1;mpfre[freque].push_front(Node(key,value,freque));mpkey[key]=mpfre[freque].begin(); //最小頻率置為1minfre=1;}}
};

下面是我們自己實現的雙向循環鏈表的寫法,代替list

構建結點和鏈表

為了方便操作,給雙向鏈表加上了虛擬的頭結點和尾結點,并在初始化的時候首尾相接。

struct Node
{int key;int value;int frequency;Node*prev;Node*next;Node():key(-1),value(-1),frequency(0),prev(nullptr),next(nullptr){}Node(int k,int v):key(k),value(v),frequency(1),prev(nullptr),next(nullptr){}
};
struct List//雙向鏈表
{int frequency;//虛擬 頭 尾 結點Node*vhead;Node*vtail;List(int f):frequency(f),vhead(new Node()),vtail(new Node()){vhead->next=vtail;vtail->prev=vhead;}
};

有了基本結構就能實現LFU以及自己手撕鏈表的一些簡單操作

這里需要用到的有,插入,刪除結點,以及鏈表判空,和返回鏈表最后一個尾結點

struct Node
{int key;int value;int frequency;Node*prev;Node*next;Node():key(-1),value(-1),frequency(0),prev(nullptr),next(nullptr){}Node(int k,int v):key(k),value(v),frequency(1),prev(nullptr),next(nullptr){}
};
struct List//雙向鏈表
{int frequency;//虛擬 頭 尾 結點Node*vhead;Node*vtail;List(int f):frequency(f),vhead(new Node()),vtail(new Node()){vhead->next=vtail;vtail->prev=vhead;}
};
class LFUCache {
private:int cap;int minfre;unordered_map<int,Node*>keymp;unordered_map<int,List*>fremp;
public:LFUCache(int capacity=10):cap(capacity),minfre(1) {}bool empty(List *ls){return ls->vhead->next==ls->vtail?true:false;}void destroy(Node*node){node->prev->next=node->next;node->next->prev=node->prev;}void addNode(Node*node){int fre=node->frequency;if(!fremp.count(fre)){ //如果結點不在fremp[fre]=new List(fre); //創建一個新鏈表}List*ls=fremp[fre];//開始插入結點node->next=ls->vhead->next;ls->vhead->next->prev=node; node->prev=ls->vhead;ls->vhead->next=node;}void popTail(){Node*tmp=fremp[minfre]->vtail->prev;destroy(tmp);keymp.erase(tmp->key);}int get(int key) {if(keymp.count(key)){//存在Node*cur=keymp[key];int val=cur->value;int fre=cur->frequency;destroy(cur);//刪完之后如果 鏈表空了,那就刪鏈表if(empty(fremp[fre])){fremp.erase(fre);if(fre==minfre){minfre++;}}//加結點cur->frequency+=1;addNode(cur);return val;}return -1;}void put(int key, int value) {if(get(key)!=-1){keymp[key]->value=value;}else{//滿了沒if(keymp.size()==cap){popTail();}Node*cur=new Node(key,value);keymp[key]=cur;minfre=1;addNode(cur);}    }
};

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

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

相關文章

C# Lamda到底是什么?!

lamda作為匿名函數&#xff0c;現在已經能夠出現子啊C#程序的任何可能位置&#xff0c;它可能作為參數為委托或其他函數復制&#xff0c;或者單獨作為表達式&#xff0c;或者承擔一些類似C中內聯函數的一些作用承擔一些簡單計算。熟練的使用Lamda表達式能夠讓減少代碼的冗余&am…

Django圖書商城系統實戰開發-總結經驗之后端開發

Django圖書商城系統實戰開發-總結經驗之后端開發 簡介 在這篇博客中&#xff0c;我將總結經驗分享后端開發Django圖書商城系統的過程。在開發過程中&#xff0c;我遇到了各種挑戰和問題&#xff0c;并且通過實踐獲得了寶貴的經驗和教訓。通過本文&#xff0c;我希望能幫助讀者…

vue3+vite配置vantUI主題

?在項目中統一配置UI主題色&#xff0c;各個組件配色統一修改 vantUI按需安裝 參考vantUI文檔 創建vantVar.less文件夾進行樣式編寫 vantVar.less :root:root{//導航--van-nav-bar-height: 44px;//按鈕--van-button-primary-color: #ffffff;--van-button-primary-backgr…

linux——mysql的高可用MHA

目錄 一、概述 一、概念 二、組成 三、特點 四、工作原理 二、案例 三、構建MHA 一、基礎環境 二、ssh免密登錄 三、主從復制 master slave1 四、MHA安裝 一、環境 二、安裝node 三、安裝manager 一、概述 一、概念 MHA&#xff08;MasterHigh Availability&a…

力扣 198. 打家劫舍

題目來源&#xff1a;https://leetcode.cn/problems/house-robber/description/ C題解&#xff1a;因為是間接偷竊&#xff0c;所以偷nums[i]家前&#xff0c;一定偷過第i-2或者i-3家&#xff0c;因為i-1不能偷。 例如12345共5家&#xff0c;先偷第1家&#xff0c;那么2不能偷…

(三)Unity開發Vision Pro——入門

3.入門 1.入門 本節涵蓋了幾個重要主題&#xff0c;可幫助您加快visionOS 平臺開發速度。在這里&#xff0c;您將找到構建第一個 Unity PolySpatial XR 應用程序的分步指南的鏈接&#xff0c;以及 PolySpatial XR 開發時的一些開發最佳實踐。 2.開發與迭代 有關先決條件、開…

顯卡nvidia-smi后 提示Faild 解決過程,包含卸載重裝NVIDIA驅動步驟

顯卡異常: 顯卡nvidia-smi后 提示Faild 解決過程&#xff0c;卸載重裝nvidia驅動步驟 文章目錄 顯卡異常: 顯卡nvidia-smi后 提示Faild 解決過程&#xff0c;卸載重裝nvidia驅動步驟 [toc]1 緣由2 解決過程3 過程所需命令4 解決4.1 把該顯卡重新拔插一下卸載NVIDIA驅動的方法&a…

單元測試優化:為什么要對程序進行測試?測試有什么好處?

單元測試&#xff08;Unit Testing&#xff09;又稱為模塊測試, 是針對程序模塊&#xff08;軟件設計的最小單位&#xff09;來進行正確性檢驗的測試工作。 程序單元是應用的最小可測試部件。簡單來說&#xff0c;就是測試數據的穩定性是否達到程序的預期。 我們日常開發時可能…

19、SQL注入之SQLMAP繞過WAF

目錄 邏輯層1、邏輯問題2、性能問題 白名單方式一&#xff1a;IP白名單方式二&#xff1a;靜態資源方式三&#xff1a;url白名單方式四: 爬蟲白名單 sqlmap在測試漏洞的時候&#xff0c;選擇了no&#xff0c;它就不會去測試其它的了&#xff0c;我們一般選擇yes&#xff0c;為了…

Deep Learning With Pytorch - 最基本的感知機、貫序模型/分類、擬合

文章目錄 如何利用pytorch創建一個簡單的網絡模型&#xff1f;Step1. 感知機&#xff0c;多層感知機&#xff08;MLP&#xff09;的基本結構Step2. 超平面 ω T ? x b 0 \omega^{T}xb0 ωT?xb0 or ω T ? x b \omega^{T}xb ωT?xb感知機函數 Step3. 利用感知機進行決策…

SpringBoot整合Minio

SpringBoot整合Minio 在企業開發中&#xff0c;我們經常會使用到文件存儲的業務&#xff0c;Minio就是一個不錯的文件存儲工具&#xff0c;下面我們來看看如何在SpringBoot中整合Minio POM pom文件指定SpringBoot項目所依賴的軟件工具包 <?xml version"1.0" …

Ubuntu上安裝RabbitMQ

在Ubuntu上安裝RabbitMQ并設置管理員用戶為"admin"&#xff0c;密碼為"123456"&#xff0c;并開啟開機自啟 更新系統軟件包列表。在終端中執行以下命令&#xff1a; sudo apt update安裝RabbitMQ服務器軟件包。運行以下命令&#xff1a; sudo apt insta…

DaVinci Resolve Studio 18 for Mac 達芬奇調色

DaVinci Resolve Studio 18是一款專業的視頻編輯和調色軟件&#xff0c;適用于電影、電視節目、廣告等各種視覺媒體的制作。它具有完整的后期制作功能&#xff0c;包括剪輯、調色、特效、音頻處理等。 以下是DaVinci Resolve Studio 18的主要特點&#xff1a; - 提供了全面的視…

Linux 設置 ssh 內網穿透

背景&#xff1a;有三臺機器A、B、C&#xff0c;機器 A 位于某局域網內&#xff0c;能夠連接到互聯網。機器 B 有公網 IP&#xff0c;能被 A 訪問到。機器 C 位于另外一個局域網內&#xff0c;能夠連接到互聯網&#xff0c;能夠訪問 B。 目標&#xff1a;以 B 為中介&#xff…

Jmeter-壓測時接口按照順序執行-臨界部分控制器

文章目錄 臨界部分控制器存在問題 臨界部分控制器 在進行壓力測試時&#xff0c;需要按照順序進行壓測&#xff0c;比如按照接口1、接口2、接口3、接口4 進行執行 查詢結果是很混亂的&#xff0c;如果請求次數少&#xff0c;可能會按照順序執行&#xff0c;但是隨著次數增加&a…

Python-OpenCV中的圖像處理-模板匹配

Python-OpenCV中的圖像處理-模板匹配 模板匹配單對象的模板匹配多對象的模板匹配 模板匹配 使用模板匹配可以在一幅圖像中查找目標函數&#xff1a; cv2.matchTemplate()&#xff0c; cv2.minMaxLoc()模板匹配是用來在一副大圖中搜尋查找模版圖像位置的方法。 OpenCV 為我們提…

無線充電底座

<項目>無線充電器 前言 個人DIY的無線充電底座&#xff08;帶磁吸&#xff09;&#xff0c;基于IP6829方案。 Drawn By:67373 硬件部分 3D模型 資料開源鏈接 https://github.com/linggan17/WirelessCharge

面試熱題(每日溫度)

請根據每日 氣溫 列表 temperatures &#xff0c;重新生成一個列表&#xff0c;要求其對應位置的輸出為&#xff1a;要想觀測到更高的氣溫&#xff0c;至少需要等待的天數。如果氣溫在這之后都不會升高&#xff0c;請在該位置用 0 來代替。 輸入: temperatures [73,74,75,71,69…

SpringBoot + Mybatis多數據源

一、配置文件 spring: # datasource: # username: root # password: 123456 # url: jdbc:mysql://127.0.0.1:3306/jun01?characterEncodingutf-8&serverTimezoneUTC # driver-class-name: com.mysql.cj.jdbc.Driverdatasource:# 數據源1onedata:jdbc-url: j…

SCF金融公鏈新加坡啟動會 鏈結創新驅動未來

新加坡迎來一場引人矚目的金融科技盛會&#xff0c;SCF金融公鏈啟動會于2023年8月13日盛大舉行。這一受矚目的活動將為金融科技領域注入新的活力&#xff0c;并為廣大投資者、合作伙伴以及關注區塊鏈發展的人士提供一個難得的交流平臺。 在SCF金融公鏈啟動會上&#xff0c; Wil…