LRU緩存機制
題目:運用你所掌握的數據結構,設計和實現一個 LRU (最近最少使用) 緩存機制。
它應該支持以下操作: 獲取數據 get 和 寫入數據 put 。
獲取數據 get(key) - 如果密鑰 (key) 存在於緩存中,則獲取密鑰的值(總是正數),否則返回 -1。
寫入數據 put(key, value) - 如果密鑰已經存在,則變更其數據值;如果密鑰不存在,則插入該組「密鑰/數據值」。
當緩存容量達到上限時,它應該在寫入新數據之前刪除最久未使用的數據值,從而為新的數據值留出空間。
示例:
LRUCache cache = new LRUCache( 2 /* 緩存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 該操作會使得密鑰 2 作廢
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 該操作會使得密鑰 1 作廢
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4
代碼:
1 class LRUCache { 2 3 public LRUCache(int capacity) { 4 5 } 6 7 public int get(int key) { 8 9 } 10 11 public void put(int key, int value) { 12 13 } 14 } 15 16 /** 17 * Your LRUCache object will be instantiated and called as such: 18 * LRUCache obj = new LRUCache(capacity); 19 * int param_1 = obj.get(key); 20 * obj.put(key,value); 21 */
LRU頁面置換算法(最近最少使用算法)(我的另一篇博文講到了LFU最不經常使用頁面置換算法,可以進行比較)
原理:
選擇最后一次訪問時間距離當前時間最長的一頁並淘汰之。即淘汰沒有使用的時間最長的頁
每頁設置訪問時間戳,每當頁面被訪問時,該頁面的時間戳被更新;
發生缺頁中斷時,淘汰時間戳最小的頁面;
如圖:圖中的頁面為三頁,依次向存儲中加入432143543215這些數字。
而存儲空間只能存儲三個頁面,所以會按照上述規則不斷淘汰已經存儲在頁面中的數字。
解題思路(logN的思路):
知道了LRU的置換規則后,由於此題需要存儲的是key和value,所以
首先,需要建一個類node,存放三樣東西,key,value,times(時間戳)
其次,選擇一種合適的數據結構來解決存儲優先級問題,此處我們采用內部是小頂堆的PriorityQueue優先級隊列用來實現times最小的元素在隊頭
但是我們會在讓新元素入隊之前可能會刪除隊列中指定元素,當然可以去遍歷隊列,但是這樣太慢了
我們可以再用一種HashMap的數據集合用來存儲節點,以便快速通過node的key來得到整個node。
最后,便是處理邏輯關系,寫題目要求的get,put方法了
解題代碼詳解(logN):
1 public class node implements Comparable<node>{ 2 private int Key;//鍵 3 private int Value;//值 4 private int Times;//時間戳 5 node() {} 6 node(int key, int value, int time) { 7 this.Key = key; 8 this.Value = value; 9 this.Times = time; 10 } 11 public int getKey() { 12 return Key; 13 } 14 15 public void setKey(int Key) { 16 this.Key = Key; 17 } 18 19 public int getValue() { 20 return Value; 21 } 22 23 public void setValue(int Value) { 24 this.Value = Value; 25 } 26 27 public int getTimes() { 28 return Times; 29 } 30 31 public void setTimes(int Times) { 32 this.Times = Times; 33 } 34 35 @Override 36 public int compareTo(node o) { 37 //實現times最小的元素在隊頭 38 return Times - o.Times; 39 } 40 } 41 42 class LRUCache { 43 PriorityQueue<node> KeyValueTimes = new PriorityQueue();//用於實現優先級順序 44 Map<Integer, node> nodeset;//用於O(1)取出某個具體的node 45 public int Capacity = 0;//我的cache中最大容量 46 public int nownum = 0;//cache的實時元素個數 47 public int tim = 0;//時間戳 48 49 public LRUCache(int capacity) { 50 this.Capacity = capacity;//設置cache容量 51 nodeset = new HashMap<Integer, node>(capacity);//用於O(1)取出某個具體的node,容量依然設置為capacity 52 } 53 54 public int get(int key) { 55 if(this.Capacity == 0)//判斷容量是否為空,為空則直接返回-1 56 return -1; 57 node nownode = nodeset.get(key);//通過HashMap,快速通過key鍵快速得到node 58 if (nownode == null) {//如果key這個鍵沒在隊列中,則返回-1 59 return -1; 60 }else{ 61 KeyValueTimes.remove(nownode);//移除隊列中當前的這個node 62 nownode.setTimes(tim++);//更新當前這個node的時間戳 63 KeyValueTimes.offer(nownode);//再把它放回去 64 } 65 return nownode.getValue(); 66 } 67 68 public void put(int key, int value) { 69 if(this.Capacity == 0)//判斷容量是否為空,為空則不進行put 70 return; 71 node thisnode = new node(key,value,tim++); 72 node oldnode = nodeset.get(key); 73 if(oldnode == null){//隊列里不存在這個key 74 if(nownum < this.Capacity){//沒裝滿 75 KeyValueTimes.offer(thisnode);//在隊列里添加新node 76 nodeset.put(key,thisnode);//在HashMap里添加新node 77 nownum++;//更新當前cache的元素個數 78 } 79 else{//裝滿了,需要LRU,最近最久為使用被移除 80 nodeset.remove(KeyValueTimes.poll().getKey());//移除隊列里的隊頭,移除HashMap對應的那個node 81 KeyValueTimes.offer(thisnode);//在隊列里添加新node 82 nodeset.put(key,thisnode);//在HashMap里添加新node 83 } 84 } 85 else{//隊列里存在這個key 86 KeyValueTimes.remove(oldnode);//移除隊列里鍵為key的node,移除HashMap對應的那個node 87 nodeset.remove(oldnode.getKey()); 88 KeyValueTimes.offer(thisnode);//在隊列里添加新node,這里新的node的value值可能會不一樣,所以更新了value 89 nodeset.put(key,thisnode);//在隊列里添加新node,這里新的node的value值可能會不一樣,所以更新了value 90 } 91 } 92 }