1. 程式人生 > >作業系統-1-儲存管理之LFU頁面置換演算法(leetcode460)

作業系統-1-儲存管理之LFU頁面置換演算法(leetcode460)

LFU快取

題目:請你為 最不經常使用(LFU)快取演算法設計並實現資料結構。它應該支援以下操作:get 和 put。
    get(key) - 如果鍵存在於快取中,則獲取鍵的值(總是正數),否則返回 -1。
    put(key, value) - 如果鍵不存在,請設定或插入值。當快取達到其容量時,則應該在插入新項之前,使最不經常使用的項無效。

            在此問題中,當存在平局(即兩個或更多個鍵具有相同使用頻率)時,應該去除 最近 最少使用的鍵。
    「項的使用次數」就是自插入該項以來對其呼叫 get 和 put 函式的次數之和。使用次數會在對應項被移除後置為 0 。

示例:  
   LFUCache cache = new LFUCache( 2 /* capacity (快取容量) */ );

     cache.put(1, 1);
     cache.put(2, 2);
     cache.get(1);       // 返回 1
     cache.put(3, 3);    // 去除 key 2
     cache.get(2);       // 返回 -1 (未找到key 2)  
     cache.get(3);       // 返回 3
     cache.put(4, 4);    // 去除 key 1
     cache.get(1);       // 返回 -1 (未找到 key 1)
     cache.get(3);       // 返回 3
     cache.get(4);       // 返回 4

程式碼:

 1 class LFUCache {
 2 
 3     public LFUCache(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 LFUCache object will be instantiated and called as such:
18  * LFUCache obj = new LFUCache(capacity);
19  * int param_1 = obj.get(key);
20  * obj.put(key,value);
21  */

LFU頁面置換演算法(最不經常使用演算法)

  原理:

    選擇到當前時間為止被訪問次數最少的頁面被置換;
    每頁設定訪問計數器,每當頁面被訪問時,該頁面的訪問計數器加1;
    發生缺頁中斷時,淘汰計數值最小的頁面,並將所有計數清零;

    如圖:圖中的頁面為三頁,依次向儲存中加入432143543215這些數字。

       而儲存空間只能儲存三個頁面,所以會按照上述規則不斷淘汰已經儲存在頁面中的數字。

    

解題思路(logN的思路):

    知道了LFU的置換規則後,由於此題需要儲存的是key和value,所以

      首先,需要建一個類node,存放四樣東西,key,value,times(訪問計數器),id(進入儲存空間的自然順序)

      其次,選擇一種合適的資料結構來解決儲存優先順序問題,此處我們採用內部是小頂堆的PriorityQueue優先順序佇列用來

         實現times最小的元素在隊頭,如果times相等,則比較先後入隊的自然順序id。

         但是我們會在讓新元素入隊之前可能會刪除佇列中指定元素,當然可以去遍歷佇列,但是這樣太慢了

         我們可以再用一種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     private int Id;//自然入隊順序標記,若訪問計數器值相同,則先淘汰id小的那個
  6     node() {}
  7     node(int key, int value, int id) {
  8         this.Key = key;
  9         this.Value = value;
 10         this.Id = id;
 11         this.Times = 1;
 12     }
 13     public int getKey() {
 14         return Key;
 15     }
 16 
 17     public void setKey(int Key) {
 18         this.Key = Key;
 19     }
 20 
 21     public int getValue() {
 22         return Value;
 23     }
 24 
 25     public void setValue(int Value) {
 26         this.Value = Value;
 27     }
 28 
 29     public int getTimes() {
 30         return Times;
 31     }
 32 
 33     public void setTimes(int Times) {
 34         this.Times = Times;
 35     }
 36     public int getId() {
 37         return Id;
 38     }
 39 
 40     public void setId(int id) {
 41         this.Id = id;
 42     }
 43 
 44     @Override
 45     public int compareTo(node o) {
 46         //實現times最小的元素在隊頭,如果times相等,則比較先後入隊順序
 47         int Timessub = Times - o.Times;
 48         return Timessub == 0 ? this.Id - o.Id: Timessub;
 49     }
 50 }
 51 
 52 class LFUCache {
 53     PriorityQueue<node> KeyValueTimes = new PriorityQueue();//用於實現優先順序順序
 54     Map<Integer, node> nodeset;//用於O(1)取出某個具體的node
 55     public int Capacity = 0;//我的cache中最大容量
 56     public int nownum = 0;//cache的實時元素個數
 57     public int id = 0;//每個node的入隊自然順序標記
 58 
 59     public LFUCache(int capacity) {
 60         this.Capacity = capacity;//設定cache容量
 61         nodeset = new HashMap<Integer, node>(capacity);//用於O(1)取出某個具體的node,容量依然設定為capacity
 62     }
 63     
 64     public int get(int key) {
 65         if(this.Capacity == 0)//判斷容量是否為空,為空則直接返回-1
 66             return -1;
 67         node nownode = nodeset.get(key);//通過HashMap,快速通過key鍵快速得到node
 68         if (nownode == null) {//如果key這個鍵沒在佇列中,則返回-1
 69             return -1;
 70         }else{
 71             KeyValueTimes.remove(nownode);//移除佇列中當前的這個node
 72             nownode.setTimes(nownode.getTimes()+1);//更新當前這個node的訪問次數
 73             nownode.setId(id++);//更新自然入隊順序
 74             KeyValueTimes.offer(nownode);//再把它放回去
 75         }
 76         return nownode.getValue();
 77     }
 78     
 79     public void put(int key, int value) {
 80         if(this.Capacity == 0)//判斷容量是否為空,為空則不進行put
 81             return;
 82         node thisnode = new node(key,value,id++);
 83         node oldnode = nodeset.get(key);
 84         if(oldnode == null){//佇列裡不存在這個key
 85             if(nownum < this.Capacity){//沒裝滿
 86                 KeyValueTimes.offer(thisnode);//在佇列裡新增新node
 87                 nodeset.put(key,thisnode);//在HashMap裡新增新node
 88                 nownum++;//更新當前cache的元素個數
 89             }
 90             else{//裝滿了,需要LRU,最近最先被移除
 91                 nodeset.remove(KeyValueTimes.poll().getKey());//移除佇列裡的隊頭,移除HashMap對應的那個node
 92                 KeyValueTimes.offer(thisnode);//在佇列裡新增新node
 93                 nodeset.put(key,thisnode);//在HashMap裡新增新node
 94             }
 95         }
 96         else{//佇列裡存在這個key
 97             thisnode.setTimes(oldnode.getTimes()+1);//將原來鍵為key的訪問次數複製給新的node
 98             KeyValueTimes.remove(oldnode);//移除佇列裡鍵為key的node,移除HashMap對應的那個node
 99             nodeset.remove(oldnode.getKey());
100             KeyValueTimes.offer(thisnode);//在佇列裡新增新node,這裡新的node的value值可能會不一樣,所以更新了value
101             nodeset.put(key,thisnode);//在佇列裡新增新node,這裡新的node的value值可能會不一樣,所以更新了value
102         }
103     }
104 }

&n