日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

LRU缓存 数据结构设计(C++)

發布時間:2023/11/30 c/c++ 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 LRU缓存 数据结构设计(C++) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

做LeetCode第146題LRU緩存,覺得收獲不小,特此記錄。

請你設計并實現一個滿足 LRU (最近最少使用) 緩存 約束的數據結構。 實現 LRUCache 類:

  • LRUCache(int capacity) 以 正整數 作為容量 capacity 初始化 LRU 緩存。
  • int get(int key) 如果關鍵字 key 存在于緩存中,則返回關鍵字的值,否則返回 -1 。
  • void put(int key, int value) 如果關鍵字 key 已經存在,則變更其數據值 value ;如果不存在,則向緩存中插入該組 key-value 。如果插入操作導致關鍵字數量超過 capacity ,則應該逐出最久未使用的關鍵字。

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

關于LRU的規則,學過操作系統的肯定都知道,此處不再贅述。

題目關鍵在于 get() 和 put() 必須O(1),這就使得我們選擇數據結構的時候需要有些考究。

首先我們回顧一下本科學過的數據結構知識,存儲這樣的多個key-value對肯定是使用線性表存儲,而線性表又分為順序表(數組)和鏈表,由于題目中涉及到了插入和刪除(逐出),且有O(1)的條件限制,所以肯定是使用鏈表來存儲。

確定了基本數據結構,我們再來想想如何使用或改造使其滿足題目需要。

首先考慮 get(),我們需要做到如果關鍵字key在緩存中,就能查找到關鍵字的值,且有O(1)的條件限制,那自然是使用哈希表,哈希表的鍵就是LRU緩存中的key自然不用說,關鍵在于 哈希表的值是什么?直觀想法那就是LRU的value唄,其實不然,因為我們其實不僅要通過key查到value,還有一個隱含的操作,即需要把我們剛剛查過的這個LRU緩存節點置于最高優先級的位置上(LRU的規則),所以該哈希表的value應該是一個鏈表節點,我們通過key查到這個鏈表節點以后,就可以對其進行操作以改變其優先級,同時我們也可以直接通過node->value來取得key對應的value值,一舉兩得。

那到底怎么體現優先級,怎么做到關鍵字的插入和逐出呢,其實很簡單,在鏈表頭部的表示優先級最高(即最近使用),在鏈表尾部的表示優先級最低(即最久未使用),這樣一來 put() 的編寫就容易了,當來一個新關鍵字的時候,就把其節點插入鏈表頭,同時更新哈希表。每當鏈表中的節點個數超過LRU的容量時,我們就刪除掉鏈表尾的的元素,同時更新哈希表。

現在我們還有一個問題需要解決,就是使用普通的單鏈表,是否能滿足put()和get()均為O(1)的題目要求。根據以上的分析,我們對鏈表會有以下操作:

  • 把一個新節點插入鏈表頭。當我們put()一個新關鍵字,其不存在于緩存中,且緩存沒有滿時,就把這個新的關鍵字節點插入鏈表頭。
  • 把一個鏈表中的節點移動到鏈表頭。當我們get()一個關鍵字,其存在于緩存中,我們就需要把這個節點從當前位置插入到鏈表頭部。
  • 刪除鏈表尾的節點。當我們put()一個新關鍵字,其不存在于緩存中,我們需要插入新節點,但此時緩存滿了,我們需要把最近最少使用的節點從鏈表中剔除掉,也就是刪除鏈表尾的節點。
  • 根據以上對鏈表的要求,可以確定只有雙鏈表能滿足要求,單鏈表、單循環鏈表、帶尾指針的單循環鏈表都無法在O(1)的時間下做到以上三個操作,大家可以模擬一下。

    確定好數據結構,代碼就很好寫了,只需按需實現LRU功能即可:

    struct doubleLinkNode {int key, value;doubleLinkNode *pre, *next; };class LRUCache { private:doubleLinkNode *head, *rear;int size, capacity;unordered_map<int, doubleLinkNode*> map;void insert(doubleLinkNode* node) {// 把一個非鏈表上的節點插入到表頭node->pre = head;node->next = head->next;head->next->pre = node;head->next = node;}void insertHead(doubleLinkNode* node) {// 把一個鏈表上的節點插入到表頭node->pre->next = node->next;node->next->pre = node->pre;insert(node);}int deleteRear() {doubleLinkNode *node = rear->pre;node->pre->next = node->next;node->next->pre = node->pre;int key = node->key;delete node;return key;}public:LRUCache(int cap): size(0), capacity(cap) {head = new doubleLinkNode();rear = new doubleLinkNode();head->next = rear;head->pre = nullptr;rear->next = nullptr;rear->pre = head;}int get(int key) {if(map.count(key)) {insertHead(map[key]);return map[key]->value;}return -1;}void put(int key, int value) {if(map.count(key)) {doubleLinkNode *node = map[key];if(node->value != value) {node->value = value;}insertHead(node);} else {doubleLinkNode *node = new doubleLinkNode;node->key = key;node->value = value;insert(node);++size;if(size > capacity) {int nodeKey = deleteRear();map.erase(nodeKey);--size;}map[key] = node;}} };

    總結

    以上是生活随笔為你收集整理的LRU缓存 数据结构设计(C++)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。