操做系統-1-存儲管理之LFU頁面置換算法(leetcode460)

LFU緩存

題目:請你爲 最不常常使用(LFU)緩存算法設計並實現數據結構。它應該支持如下操做:get 和 put。
    get(key) - 若是鍵存在於緩存中,則獲取鍵的值(老是正數),不然返回 -1。
    put(key, value) - 若是鍵不存在,請設置或插入值。當緩存達到其容量時,則應該在插入新項以前,使最不常常使用的項無效。css

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

示例:  
   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頁面置換算法(最不常常使用算法)數據結構

  原理:ide

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

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

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

    

解題思路(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 }
相關文章
相關標籤/搜索