页面置换算法之LRU缓存机制

不羁的心 提交于 2019-11-26 19:36:29

LRU缓存机制

LRU是页面置换算法的其中一个,是一种最近最少使用的缓存机制,它支持以下操作
获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。

我们使用O(1)的时间复杂度完成这两种操作

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

思路:
使用两个数据结构,一个是队列,一个是哈希map
确保通过队列的结点可以找到哈希map,通过哈希map也可以找到队列的结点,我们这样定义
//pair 中的first和second我叫做 key 和value
list< pair<int ,int >> queue;

//m的first是key,这个key和queue中pair的key相对应,m的second的是list的迭代器
unordered_map<int, list<pair<int,int> >::iterator > m;

从上面的结构我们可以发现,通过key经过哈希map的索引我们找到list的迭代器,通过list的迭代器我们就可以找到pair结构的second,也就是value,同样的,在已知list的迭代器的情况下,我们可以通过迭代器中的key去索引到map的迭代器。

在这里插入图片描述
根据上面的叙述我们可以捋一下程序的大致流程:
set操作:
我们用一个队列去纪录我们的数据使用的顺序,在队列的最尾端是最早使用的数据,也就是最近使用最少的数据。
1.当set一个key-value后,我们首先寻找这个key是否在哈希map中能找到,如果找到了就找到了对应list中的迭代器了,我们把list对应的那个结点调整到队列首,表示最新使用的,并且把哈希map中key对应的值重新指向调整后的结点
2.如果没有找到说明要增加新数据,先判断容量够不够用,如果够用(size < cap)直接size要++,同时要向队列中插入一个key-value,并且哈希map中也要生成一个key-value指向list中的结点,如果容量(size == cap)不够用就要淘汰最不经常使用的结点(实际上就是list中的尾结点),怎么删除呢? 先用list中尾结点的key值把哈希map中的映射关系删除掉,然后再删除list中的尾结点,最后再添加新结点。

get操作:
get操作就是读取对应key值对应的value值,需要做的就是每次读取后,如果key值存在就把key值对应的list结点调整到首部,表示最新使用的,并且调整哈希map中key对应的结点
如果key没有对应的结点就直接返回-1即可。

代码

class LRUCache {
public:
    LRUCache(int capacity):cap(capacity),size(0) {
           if(cap == 0)
               assert(false);
    }
    
    int get(int key) {
        int value = -1;
        auto it = key_list_map.find(key);
        if(it==key_list_map.end())
        {
            return -1;
        }
        else
        {
            value = it->second->second;  
            ls.erase(it->second);
            ls.push_front(pair<int,int>(key,value));
            it->second = ls.begin();
  
            return value;
        }
    }
    
    void put(int key, int value) {
        auto it = key_list_map.find(key);
        if(it==key_list_map.end())
        {
            if(cap == size)
            {
                //用list中的key删除unorderedmap中的关系
                key_list_map.erase(ls.back().first);
                ls.pop_back();
            }
            else
            {
                ++size;
            }
        }
        else
        {
            ls.erase(it->second);//用迭代器删除
        }
        //重新入一个新的键值对
        //重新建立映射关系
        ls.push_front(pair<int,int>(key,value));
         key_list_map[key] = ls.begin();
    }
    
    int cap;
    int size;
    std::list< std::pair<int,int> > ls;
    std::unordered_map<int,std::list< std::pair<int,int> >::iterator> key_list_map; 
    
};

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!