題目:
運用你所掌握的資料結構,設計和實現乙個 lru (最近最少使用) 快取機制 。
實現 lrucache 類:
lrucache(int capacity) 以正整數作為容量 capacity 初始化 lru 快取
int get(int key) 如果關鍵字 key 存在於快取中,則返回關鍵字的值,否則返回 -1 。
void put(int key, int value) 如果關鍵字已經存在,則變更其資料值;如果關鍵字不存在,則插入該組「關鍵字-值」。當快取容量達到上限時,它應該在寫入新資料之前刪除最久未使用的資料值,從而為新的資料值留出空間。
高階:你是否可以在 o(1) 時間複雜度內完成這兩種操作?
示例:輸入
["lrucache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
輸出[null, null, null, 1, null, -1, null, -1, 3, 4]
解釋lrucache lrucache = new lrucache(2);
lrucache.put(1, 1); // 快取是
lrucache.put(2, 2); // 快取是
lrucache.get(1); // 返回 1
lrucache.put(3, 3); // 該操作會使得關鍵字 2 作廢,快取是
lrucache.get(2); // 返回 -1 (未找到)
lrucache.put(4, 4); // 該操作會使得關鍵字 1 作廢,快取是
lrucache.get(1); // 返回 -1 (未找到)
lrucache.get(3); // 返回 3
lrucache.get(4); // 返回 4
演算法:雜湊表 + 雙向鍊錶
lru 快取機制可以通過雜湊表輔以雙向鍊錶實現,我們用乙個雜湊表和乙個雙向鍊錶維護所有在快取中的鍵值對。
雙向鍊錶按照被使用的順序儲存了這些鍵值對,靠近頭部的鍵值對是最近使用的,而靠近尾部的鍵值對是最久未使用的。
雜湊錶即為普通的雜湊對映(hashmap),通過快取資料的鍵對映到其在雙向鍊錶中的位置。
這樣以來,我們首先使用雜湊表進行定位,找出快取項在雙向鍊錶中的位置,隨後將其移動到雙向鍊錶的頭部,即可在 o(1)o(1) 的時間內完成 get 或者 put 操作。具體的方法如下:
對於 get 操作,首先判斷 key 是否存在:
如果 key 不存在,則返回 -1−1;
如果 key 存在,則 key 對應的節點是最近被使用的節點。通過雜湊表定位到該節點在雙向鍊錶中的位置,並將其移動到雙向鍊錶的頭部,最後返回該節點的值。
對於 put 操作,首先判斷 key 是否存在:
如果 key 不存在,使用 key 和 value 建立乙個新的節點,在雙向鍊錶的頭部新增該節點,並將 key 和該節點新增進雜湊表中。然後判斷雙向鍊錶的節點數是否超出容量,如果超出容量,則刪除雙向鍊錶的尾部節點,並刪除雜湊表中對應的項;
如果 key 存在,則與 get 操作類似,先通過雜湊表定位,再將對應的節點的值更新為 value,並將該節點移到雙向鍊錶的頭部。
上述各項操作中,訪問雜湊表的時間複雜度為 o(1)o(1),在雙向鍊錶的頭部新增節點、在雙向鍊錶的尾部刪除節點的複雜度也為 o(1)o(1)。而將乙個節點移到雙向鍊錶的頭部,可以分成「刪除該節點」和「在雙向鍊錶的頭部新增節點」兩步操作,都可以在 o(1)o(1) 時間內完成。
小貼士在雙向鍊錶的實現中,使用乙個偽頭部(dummy head)和偽尾部(dummy tail)標記界限,這樣在新增節點和刪除節點的時候就不需要檢查相鄰的節點是否存在。
public class lrucache
public dlinkednode(int _key, int _value)
}private mapcache = new hashmap();
private int size;
private int capacity;
private dlinkednode head, tail;
public lrucache(int capacity)
public int get(int key)
// 如果 key 存在,先通過雜湊表定位,再移到頭部
movetohead(node);
return node.value;
}public void put(int key, int value)
}else
}private void addtohead(dlinkednode node)
private void removenode(dlinkednode node)
private void movetohead(dlinkednode node)
private dlinkednode removetail()
}
複雜度分析
時間複雜度:對於 put 和 get 都是 o(1)。
空間複雜度:o(capacity),因為雜湊表和雙向鍊錶最多儲存 capacity+1 個元素。
LRU快取機制 LeetCode
演算法思想 1 關鍵字存在 變更資料。2 關鍵字不存在 判斷快取容量是否達到上限 達到了上限,則應該刪除最久未使用的資料 未達上限,直接新增。get約束條件如下 如果關鍵字在快取,獲取該關鍵字的值,否則返回 1。此題目要求的資料結構需要滿足 查詢快 插入 刪除快且有順序之分的特點。於是結合兩個資料結...
LeetCode 經典題 LRU快取
lru演算法應該對所有計算機 軟工的同學都不陌生了。那麼要如何實現lru演算法呢?lru演算法需要設計乙個資料結構,這個資料結構有兩個操作,乙個是get key 獲取key對應的value,如果key不存在則返回 1 put key,value 存入鍵值對 以上兩個操作的複雜度都應該為o 1 分析上...
leetcode146 LRU快取機制
運用你所掌握的資料結構,設計和實現乙個 lru 最近最少使用 快取機制。它應該支援以下操作 獲取資料 get 和 寫入資料 put 獲取資料 get key 如果金鑰 key 存在於快取中,則獲取金鑰的值 總是正數 否則返回 1。寫入資料 put key,value 如果金鑰不存在,則寫入其資料值。...