運用你所掌握的資料結構,設計和實現乙個lru (最近最少使用) 快取機制 。它應該支援以下操作: 獲取資料get
和 寫入資料put
。
獲取資料get(key)
- 如果金鑰 (key) 存在於快取中,則獲取金鑰的值(總是正數),否則返回 -1。
寫入資料put(key, value)
- 如果金鑰不存在,則寫入其資料值。當快取容量達到上限時,它應該在寫入新資料之前刪除最近最少使用的資料值,從而為新的資料值留出空間。
高階:你是否可以在 o(1) 時間複雜度內完成這兩種操作?
思路:
lru實現採用雙向鍊錶 + hash_map 來進行實現。這裡採用雙向鍊錶的原因是:如果採用普通的單鏈表,則刪除節點的時候需要從表頭開始遍歷查詢,效率為o(n),採用雙向鍊錶可以直接改變節點的前驅的指標指向進行刪除達到o(1)的效率。使用map來儲存節點的key、value值便於能在o(logn)的時間查詢元素,對應get操作。
**:
#define _silence_stdext_hash_deprecation_warnings
#include#include#includeusing namespace std;
struct cachenode
};class lrucache
int get(int key);
void set(int key,int value);
void remove(cachenode *node);
void sethead(cachenode *node);
cachenode *gethead();
private:
int size;//最大快取數
cachenode *head, *tail;
hash_mapmap;//int是關鍵字
};int lrucache::get(int key)
return node->value;
} else
return -1;
}void lrucache::set(int key, int value)
node->value = value;
remove(node);
sethead(node);
} else
sethead(node);
map[key] = node; }}
void lrucache::remove(cachenode *node)
void lrucache::sethead(cachenode *node)
else }
cachenode* lrucache::gethead()
int main(void)
設計並實現最少訪問頻率(lfu)快取的資料結構。lfu的每個資料塊都有乙個引用計數,所有資料塊按照引用計數排序,具有相同引用計數的資料塊則按照時間進行排序。它應該支援以下操作:get
和put
。
get(key)
- 如果鍵存在於快取中,則獲取鍵的值(總是正數),否則返回 -1。
put(key, value)
- 如果鍵不存在,請設定或插入值。當快取達到其容量時,它應該在插入新專案之前,使最不經常使用的專案無效。在此問題中,當存在平局(即兩個或更多個鍵具有相同使用頻率)時,最近最少使用的鍵將被去除。
高階:你是否可以在 o(1) 時間複雜度內執行兩項操作?
思路:
lru實現採用2個雙向鍊錶 + 2個hash_map 來進行實現。乙個雙向鍊錶用來鏈結各個頻率的鍊錶,另乙個鍊錶用來鏈結相同訪問頻率的各個記錄。這兩個鍊錶是巢狀關係。
兩個hash_map分別是存放關鍵字和對應的node,以及node所對應的頻率鏈。
每次新加入節點都是從頭部加入,所以對於訪問次數相同的關鍵字,刪除的是鍊錶尾部的那個,所以鍊錶需要有指針對頭尾進行記錄。
**:
#define _silence_stdext_hash_deprecation_warnings
#include#include#includeusing namespace std;
class node
public:
int key;//關鍵字
int value;//值
int times;//訪問的次數
node *pre;//前乙個節點
node *next;//後乙個節點
};class nodelist
void deletenode(node *node);
public:
node *head;
node *tail;
nodelist *pre;
nodelist *next;
};nodelist::nodelist(node *node)
void nodelist::addnodefromhead(node *newhead)
void nodelist::deletenode(node *node)
//刪掉這個節點之後,鍊錶中還至少有乙個節點
else
else if (node == tail)
else
}node->next = nullptr;
node->pre = nullptr;
}class lfucache ;
lfucache::lfucache(int c)
void lfucache::set(int key, int value)
else//緩衝區之前沒有這個關鍵字
node *node = new node(key, value, 1);//新建乙個節點,訪問次數為1
if (headlist == nullptr)
headlist = new nodelist(node);
else
}records[key] = node;
heads[node] = headlist;
size++; }}
//oldnodelist是node原來屬於的那個nodelist,當訪問數加一之後需要找到下乙個nodelist
void lfucache::move(node *node, nodelist* oldnodelist)
else//oldnodelist有下乙個節點
else
}}//把乙個node從lodelist裡面delete之後,然後呼叫下面這個
//方法進行判斷是不是連整個鍊錶不要了
bool lfucache::modifyheadlist(nodelist *nodelist)
else
return true;
} return false;
}int lfucache::get(int key)
int main(void)
快取淘汰演算法 LRU 和 LFU
lru least recently used 即最近最少使用演算法,是一種常見的 cache 頁面置換演算法,有利於提高 cache 命中率。lru 的演算法思想 對於每個頁面,記錄該頁面自上一次被訪問以來所經歷的時間 t 當淘汰乙個頁面時,應選擇所有頁面中其 t 值最大的頁面,即記憶體中最近一段...
LRU與LFU快取替換演算法C 實現
lfu實現 資料結構unordered map dqueue unordered map的鍵為key,值為node 方便快速訪問資料 dqueue雙端佇列,快速刪除 插入node 簡單的lru是存在缺點的,比如有某一次快取操作是完全隨機的,但是lru快取中替換為了這一次隨機的訪問資料 指的是,只有這...
快取策略之LRU和LFU
快取,就是把資料儲存在本地,簡單實用key value做乙個對映就好了。但為什麼還要快取策略,因為快取的大小需要限制,否則儲存的東西只增不減,時間一長就有很多記憶體浪費。因為資源總是有限,所以優化,因為優化所以複雜 這個是least recently used的縮寫,即最近最少使用。它的邏輯很簡單 ...