1. 程式人生 > 其它 >LRU演算法及實現

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 快取

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); // 快取是 {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());
	}
}

最後執行結果
在這裡插入圖片描述