操作系统-2-存储管理之LRU页面置换算法(LeetCode146)

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 }
原文地址:https://www.cnblogs.com/qinqin-me/p/12699642.html