LRU演算法及實現
技術標籤:leetcode程式設計與演算法Javajava演算法
1.LRU演算法是什麼?
LRU是Least Recently Used的縮寫,即最近最少使用,是一種常用的頁面置換演算法
選擇最近最久未使用的資料予以淘汰。
除此之外,在redis快取中也是用到了這種演算法。redis有讀寫兩個操作,然後快取是有空間限制的,大小會有一定上線的,
2.演算法來源
來自https://leetcode-cn.com/problems/lru-cache/
運用你所掌握的資料結構,設計和實現一個 LRU (最近最少使用) 快取機制 。
實現 LRUCache 類:
LRUCache(int capacity) 以正整數作為容量 capacity 初始化 LRU 快取
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); // 快取是 {1=1}
lRUCache.put(2, 2); // 快取是 {1=1, 2=2}
lRUCache. get(1); // 返回 1
lRUCache.put(3, 3); // 該操作會使得關鍵字 2 作廢,快取是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 該操作會使得關鍵字 1 作廢,快取是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
3.設計思想
進階:你是否可以在 O(1) 時間複雜度內完成這兩種操作?
這裡要求一次命中,寫要寫成功,讀也要讀成功。
思考java中什麼樣的資料結構能一次找到。
1.所謂快取,必須要有讀+寫操作,按照命中率的思路考慮,寫操作時間複雜度都需要為O(1)
2.特性要求
2.1.必須要有順序之分,一區分最近使用的和很久沒有使用的資料排序。
2.2.寫和讀操作一次搞定
2.3.**如果容量(坑位)滿了要刪除最不常用的資料,每次新訪問還要把新的資料插入到隊頭(**按照業務你自己設定左右那一邊是隊頭)
查詢快、插入快、刪除快,且還需要先後排序------->什麼樣的資料結構可以滿足這個問題?
你是否可以在O(1)時間複雜度內完成這個操作?
如果一次就可以找到,你覺得什麼資料結構最合適??
雜湊儲存結構
LRU的演算法核心是雜湊+連結串列
本質就是HashMap+DoubleLinkedList,時間複雜度O(1),雜湊表+雙向連結串列的結合體
雜湊能夠實現O(1)時間內的查詢,雙向連結串列能夠保證增刪比較快。即查詢用雜湊,增刪用連結串列。
4.編碼手寫如何實現LRU
巧用LinkedHashMap完成lru演算法
檢視API,可以看到繼承自HashMap
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
這種資料結構非常適合於構造LRU演算法
eldest是最近最少使用的entry插入到map中。
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
以下是程式碼實現
public class LRUCacheDemo<K,V> extends LinkedHashMap<K,V>{
private int capacity;
public LRUCacheDemo(int capacity){
//引數1,坑位,傳過來的值,引數2,載入因子,引數3,訪問順序
super(capacity,0.75F,true);
this.capacity=capacity;
}
//怎麼彈出去一個坑位
@Override
protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
return super.size()>capacity;
}
public static void main(String[] args) {
//坑位是3個
LRUCacheDemo lruCacheDemo=new LRUCacheDemo<>(3);
lruCacheDemo.put(1,"a");
lruCacheDemo.put(2,"b");
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.keySet());
lruCacheDemo.put(4,"d");
System.out.println(lruCacheDemo.keySet());
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.keySet());
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.keySet());
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.keySet());
lruCacheDemo.put(3,"c");
System.out.println(lruCacheDemo.keySet());
lruCacheDemo.put(5,"x");
System.out.println(lruCacheDemo.keySet());
}
}
最後執行結果