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();

转载于:https://www.cnblogs.com/dreamrun/p/4522663.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值