• No.146 LRU Cache


    No.146 LRU Cache

    Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.

    get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
    set(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

    解析:

      这题其实并没有考察什么特别难的算法,主要还是考察数据结构的设计问题。

      LRU算法:最近最少使用,替换在内存中,但又不使用的内存块
       题意理解:key应该是物理块块号,而value为内存块号,相当于下标索引【但对set函数明显不对】
                 正确理解应该为:key为关键字,value为key对应的值
       思考:用什么数据结构来存?

       设计:【思路没有任何难度,就是使用什么数据结构的问题!!!】

          越靠前,越新

          若不存在且未满,头插
          若不存在且已满,置最后一个无效pop_back(),头插
          若存在,原来的删除,头插


         最开始想到使用vector,后来想到头插的话,全部都要移动,还是用链表list比较好,有push_front;但是用list仍然是超时,最后根据网上查到的,使用双向链表和哈希表,来提高性能。

      第一种提交方案【超时】:
     

      1 #include "stdafx.h"
      2 #include <list>
      3 #include <iostream>
      4 #include <algorithm>
      5 using namespace std;
      6 /*
      7     LRU算法:最近最少使用,替换在内存中,但又不使用的内存块
      8     题意理解:key应该是物理块块号,而value为内存块号,相当于下标索引【貌似有问题】
      9               key为关键字,value为key对应的值
     10     思考:用什么数据结构来存?
     11           最开始想到使用vector,后来想到头插的话,全部都要移动,还是用链表list比较好,有push_front
     12           list越前越新
     13     用vector:
     14         若不存在且未满,头插
     15         若不存在且已满,置最后一个无效pop_back(),头插
     16         若存在,原来的删除,头插
     17 */
     18 struct entry
     19 {
     20     int key;
     21     int value;
     22 };
     23 class LRUCache
     24 {
     25 public:
     26     LRUCache(int capacity)
     27     {
     28         capa = capacity;//
     29         size = 0;
     30     }
     31 
     32     int get(int key)
     33     {//输入:物理块号key
     34      //输出:若物理块号key在内存中,则输出其下标value(总为正);否则,输出-1
     35         auto index = find_if(cache.begin(),cache.end(), [key](const struct entry &a){return a.key == key;});
     36         if(index == cache.end())
     37             return -1;
     38         else
     39             return (*index).value;
     40     }
     41 
     42     void set(int key, int value)
     43     {//输入:物理块号key,
     44      //输出:若物理块号key不在内存中,将value插入cache中。当cache达到其容量capacity,将最近最少使用的在新的插入前置为无效
     45         auto index = find_if(cache.begin(),cache.end(), [key](const struct entry &a){return a.key == key;});//注意写法,尤其是要return
     46         entry data;
     47         data.key = key;
     48         data.value = value;
     49 
     50         if(index == cache.end())//key不存在
     51         {
     52             if(size < capa)//未满
     53             {
     54                 cache.push_front(data);
     55                 size++;
     56             }
     57             else//已满,交换
     58             {
     59                 cache.pop_back();
     60                 cache.push_front(data);
     61             }
     62         }
     63         else//key存在
     64         {
     65             cache.erase(index);
     66             cache.push_front(data);
     67         }            
     68     }
     69     void show()
     70     {
     71         for(auto const &a : cache)
     72             cout << a.key << " ";
     73         cout << endl;
     74         //for(auto const &a : cache)//测试的等同key,可以暂时不显示
     75         //    cout << a.value << " ";
     76         cout << endl;
     77     }
     78 private:
     79     int size;//cache中现有数据块
     80     int capa;//cache容量
     81     list<entry> cache;
     82 };
     83 
     84 int main()
     85 {
     86     LRUCache test(3);
     87 
     88     test.set(4, 4);
     89     test.show();//4
     90 
     91     test.set(3,3);
     92     test.show();//3 4
     93     
     94 
     95     test.set(4,4);
     96     test.show();//4 3
     97 
     98     test.set(2,2);
     99     test.show();//2 4 3 
    100 
    101     test.set(3,3);
    102     test.show();//3 2 4  
    103 
    104     test.set(1,1);
    105     test.show();//1 3 2 
    106 
    107     test.set(4,4);
    108     test.show();//4 1 3 
    109 
    110     test.set(2,2);
    111     test.show();//2 4 1 
    112 
    113 
    114     return 0;
    115 }
    View Code

      为了使查找、插入和删除都有较高的性能,我们使用一个双向链表 (std::list) 和一个哈希表(std::unordered_map),因为:
      • 哈希表保存每个节点的地址,可以基本保证在 O(1) 时间内查找节点【不用map查找要遍历,复杂度太高】
      • 双向链表插入和删除效率高,单向链表插入和删除时,还要查找节点的前驱节点
      具体实现细节:
      • 越靠近链表头部,表示节点上次访问距离现在时间最短,尾部的节点表示最近访问最少
      • 访问节点时,如果节点存在,把该节点交换到链表头部,同时更新 hash 表中该节点的地址
      • 插入节点时,如果 cache 的 size 达到了上限 capacity,则删除尾部节点,同时要在 hash 表中删除对应的项;新节点插入链表头部

      1 #include "stdafx.h"
      2 #include <list>
      3 #include <unordered_map>
      4 #include <iostream>
      5 #include <algorithm>
      6 using namespace std;
      7 /*
      8     LRU算法:最近最少使用,替换在内存中,但又不使用的内存块
      9     题意理解:key应该是物理块块号,而value为内存块号,相当于下标索引【貌似有问题】
     10               key为关键字,value为key对应的值
     11     思考:用什么数据结构来存?
     12           最开始想到使用vector,后来想到头插的话,全部都要移动,还是用链表list比较好,有push_front
     13           list越前越新
     14     用vector:
     15         若不存在且未满,头插
     16         若不存在且已满,置最后一个无效pop_back(),头插
     17         若存在,原来的删除,头插
     18 */
     19 class LRUCache
     20 {
     21 private:
     22     struct CacheNode
     23     {
     24         int key;
     25         int value;
     26         CacheNode(int k, int v):key(k), value(v){}//写个构造函数,方便!!
     27     };
     28 public:
     29     LRUCache(int capacity)
     30     {
     31         this->capacity = capacity;//this的使用,用指针
     32     }
     33 
     34     int get(int key)
     35     {
     36         if(cacheMap.find(key) == cacheMap.end())
     37             return -1;
     38         //把当前访问的节点移到链表头部,并且更新map中该节点的地址!!!自己没想到,访问也是要更新的
     39 
     40         cacheList.push_front(*(cacheMap[key]));//一定要先插,后删,否则,会出现访问问题
     41         cacheList.erase(cacheMap[key]);//!!!要是直接移动,就更好了
     42         //cacheList.splice(cacheList.begin(), cacheList, cacheMap[key]);//【用splice()反而超时】
     43         //splice()链表独有的类型;移动:将cacheMap[key]指向的元素移动到cacheList.begin()之前的位置
     44         cacheMap[key] = cacheList.begin();
     45         return cacheMap[key]->value;
     46     }
     47 
     48     void set(int key, int value)
     49     {
     50         if(cacheMap.find(key) == cacheMap.end())//不存在
     51         {
     52             if(cacheList.size() == capacity)//已满
     53             {
     54                 cacheMap.erase(cacheList.back().key);
     55                 cacheList.pop_back();
     56             }
     57             //头插,且更新map
     58             cacheList.push_front(CacheNode(key,value));
     59             cacheMap[key] = cacheList.begin();
     60         }
     61         else
     62         {//存在,则更新位置
     63             cacheMap[key]->value = value;//!!!
     64             //cacheList.splice(cacheList.begin(), cacheList, cacheMap[key]);
     65             cacheList.push_front(*(cacheMap[key]));
     66             cacheList.erase(cacheMap[key]);//!!!
     67             cacheMap[key] = cacheList.begin();
     68 
     69         }
     70     }
     71 
     72      void show()
     73      {
     74         for(auto const &a : cacheList)
     75             cout << a.key << " ";
     76         cout << endl;
     77         //for(auto const &a : cache)//测试的等同key,可以暂时不显示
     78         //    cout << a.value << " ";
     79         cout << endl;
     80      }
     81 
     82 private:
     83     list<CacheNode> cacheList;
     84     unordered_map<int, list<CacheNode>::iterator> cacheMap;
     85     int capacity;
     86 };
     87 
     88 int main()
     89 {
     90     LRUCache test(3);
     91 
     92     test.set(4, 4);
     93     test.show();//4
     94 
     95     test.set(3,3);
     96     test.show();//3 4
     97     
     98 
     99     test.set(4,4);
    100     test.show();//4 3
    101 
    102     test.set(2,2);
    103     test.show();//2 4 3 
    104 
    105     test.set(3,3);
    106     test.show();//3 2 4  
    107 
    108     test.set(1,1);
    109     test.show();//1 3 2 
    110 
    111     test.set(4,4);
    112     test.show();//4 1 3 
    113 
    114     test.set(2,2);
    115     test.show();//2 4 1 
    116 
    117 
    118     return 0;
    119 }
  • 相关阅读:
    LeetCode Algorithm
    实现QObject与JavaScript通讯(基于QWebEngine + QWebChannel)
    Linux工具参考篇(网摘)
    GO 语言简介(网摘)
    Qt窗体引用window自带阴影边框效果
    无插件VIM编程技巧(网摘)
    ASP.NET 5基础之中间件
    ASP.NET Core 1.0基础之应用启动
    理解ASP.NET 5 Web Apps
    DNX 概览
  • 原文地址:https://www.cnblogs.com/dreamrun/p/4522663.html
Copyright © 2020-2023  润新知