leetcode 146. LRU Cache 、460. LFU Cache
LRU算法是首先淘汰最长时间未被使用的页面,而LFU是先淘汰一定时间内被访问次数最少的页面,如果存在使用频度相同的多个项目,则移除最近最少使用(Least Recently Used)的项目。
LFU在频度相同的时候与LRU类似。
146. LRU Cache
1.stl中list是双向链表,slist才是单向链表。
rbegin():返回尾元素的逆向迭代器指针
end():返回尾元素之后位置的迭代器指针
https://www.cnblogs.com/Kobe10/p/5780095.html
2.使用hash是让查询的时间复杂度为O(1),使用双向链表,是cache里面数值移动的时间复杂度为O(1)。如果采用数组,移动的时间复杂度为O(n),因为你需要将第i个放到首位,然后原本的前i-1个都需要逐个向后移动一个。队列的操作也是如此,你需要先弹出前i-1同时都弹到队尾,然后取出当前的i,然后把原本i后面剩下的也都弹出放到队尾,再把原本的i放到头部。
3.另外一个非常关键的降低时间复杂度的方法是在hash中保存那个key在链表中对应的指针, 我们知道链表要查找一个结点的时间复杂度是O(n), 所以当我们需要移动一个结点到链表首部的时候, 如果直接在链表中查询那个key所对于的结点, 然后再移动, 这样时间复杂度将会是O(n), 而一个很好的改进方法是在hash表中存储那个key在链表中结点的指针, 这样就可以在O(1)的时间内移动结点到链表首部.
4.unordered_map: size(): 返回有效元素个数
find():通过给定主键查找元素,没找到:返回unordered_map::end
find返回的是一个迭代器,这个迭代器用->first、->second分别访问key和value
count():返回匹配给定主键的元素的个数,也就是同一个key,对应多少个value
unordered_map可以通过主键来访问,返回是value值。比如m[1] = 1。find返回的则是迭代器,且迭代器指向的是键值对,first对应键,second对应值。
rbegin是最后一个元素
m.find(key) != m.end()就不在容器中
erase()可以删除指针,也可以删除key
5.双向链表list的splice函数,void splice (iterator position, list& x, iterator i); //将列表x中迭代器 i 指向的元素移到当前list的position指向的位置处,由于i指向的元素从列表x中被移除,所以迭代器 i 此时是invalid的;position是当前列表的迭代器,i是列表x的迭代器。时间复杂度为0(1)
list:pop_back删除最后一个元素
push_front在前面加入
6.pair、make_pair: std::pair主要的作用是将两个数据组合成一个数据,两个数据可以是同一类型或者不同类型,->first、->second用于访问pair中的第一个和第二个元素
make_pair:无需写出型别, 就可以生成一个pair对象
例: std::make_pair(42, '@');
而不必费力写成:
std::pair<int, char>(42, '@')
7.auto:可以在声明变量的时候根据变量初始值的类型自动为此变量选择匹配的类型
8.迭代器:是一种检查容器内元素并遍历元素的数据类型。C++更趋向于使用迭代器而不是下标操作,因为标准库为每一种标准容器(如vector)定义了一种迭代器类型,而只有少数容器(如vector)支持下标操作访问容器元素。
9. l.splice(l.begin(), l, it->second);这一行将l的位置移动了,主要是先删除原先的位置,再放到头部。我以为那个迭代器会失效,认为这个地方应该更新hash里面的迭代器,所以在后面一行增加了m[key] = l.begin();。但是经过测试没有失效
测试的代码如下:
int main(){
LRUCache debug();
// cout << debug.cap << endl;
debug.put(,);
debug.put(,);
debug.put(,);
cout << debug.l.rbegin()->first << endl;
cout << debug.get() << endl;
cout << debug.l.rbegin()->first << endl;
}
测试的结果:
在测试的时候,自己对构造函数的初始化不是很了解,所以最开始的测试代码错误写成如下:
int main(){
LRUCache debug;
debug.LRUCache();
}
正确代码:
class LRUCache{
public:
LRUCache(int capacity) {
cap = capacity;
} int get(int key) {
auto it = m.find(key);
if (it == m.end()) return -;
l.splice(l.begin(), l, it->second);
//m[key] = l.begin(); 自己以为迭代器会失效,但是没有
return it->second->second;
} void put(int key, int value) {
auto it = m.find(key);
if (it != m.end()) l.erase(it->second);
l.push_front(make_pair(key, value));
m[key] = l.begin();
if (m.size() > cap) {
int k = l.rbegin()->first;
l.pop_back();
m.erase(k);
}
} private:
int cap;
list<pair<int, int>> l;
unordered_map<int, list<pair<int, int>>::iterator> m;
};
https://www.cnblogs.com/lalalabi/p/5060210.html
http://www.cnblogs.com/grandyang/p/4587511.html
https://blog.csdn.net/qq508618087/article/details/50995188
460. LFU Cache
https://www.cnblogs.com/grandyang/p/6258459.html
因为需要判断key出现的频率,所以必须存储不同的频率。并且同一个频率,可能有不同的key,也就是说freq至少是一个unordered_map<int,vector<int>>的形式。但是你需要删除,vector删除不是O(1)的时间复杂度,双向链表list是,所以是unordered_map<int,list<int>>。要删除,这就和lru类似了,你需要存储key在list中的位置,直接找到这个位置,所以还需要unordered_map<int,list<int>::iterator>。对应的还需要存储key、value,并且需要找到每个key对应的频率,所以需要存储vector<key,pair<value,freq>>。
错误代码:
class LFUCache {
public:
LFUCache(int capacity) {
cap = capacity;
} int get(int key) {
auto it = m.find(key);
if(it == m.end())
return -;
int fre = it->second->second;
freq[fre].erase(iter[key]);
fre++;
freq[fre].push_front(key);
iter[key] = freq[fre].begin();
it->second->second++;
if(freq[min_freq].size() == )
min_freq++;
return it->second->first;
} void put(int key, int value) {
auto it = m.find(key);
if(get(key) != -){
m[key]->first = value;
return;
}
if(m.size() > cap){
int k = freq[min_freq].rbegin();
freq.pop_back();
m.erase(k);
iter.erase(k);
}
m[key] = {value,};
freq[].push_front(key);
iter[key] = freq[].begin();
min_freq = ;
} private:
int cap,min_freq;
unordered_map<int,pair<int,int>> m;
unordered_map<int,list<int>> freq;
unordered_map<int,list<int>::iterator> iter;
};
m<key,pair<value,freq>>
freq<freq,list<key>>
iter<key,freq的list中的位置>
正确代码:
class LFUCache {
public:
LFUCache(int capacity) {
cap = capacity;
} int get(int key) {
auto it = m.find(key);
if(it == m.end())
return -;
int fre = it->second.second;
freq[fre].erase(iter[key]);
fre++;
freq[fre].push_front(key);
iter[key] = freq[fre].begin();
it->second.second++;
if(freq[min_freq].size() == )
min_freq++;
return it->second.first;
} void put(int key, int value) {
if(cap <= )
return;
if(get(key) != -){
m[key].first = value;
return;
}
if(m.size() >= cap){
int k = *freq[min_freq].rbegin();
freq[min_freq].pop_back();
m.erase(k);
iter.erase(k);
}
m[key] = {value,};
freq[].push_front(key);
iter[key] = freq[].begin();
min_freq = ;
} private:
int cap,min_freq;
unordered_map<int,pair<int,int>> m;
unordered_map<int,list<int>> freq;
unordered_map<int,list<int>::iterator> iter;
};
之间写的一个代码:
hash:存储的key、value、freq
freq:存储的freq、key,也就是说出现1次的所有key在一起,用list连接
class LFUCache {
public:
LFUCache(int capacity) {
cap = capacity;
} int get(int key) {
auto it = hash.find(key);
if(it == hash.end())
return -1; 这段代码以下处理的都是已有key的情况 freq[hash[key].second].erase(iter[key]); 如果这个key已经有了,那次数就要加1。所以必须把freq里面的对应次数的key先删除,然后更新hash里key对应的次数,
++hash[key].second; 同时在freq中将这个key连接次数加1的地方
freq[hash[key].second].push_back(key);
iter[key] = --freq[hash[key].second].end(); 因为这个key在freq中变换了位置,那对应的迭代器地址也应该改变,是push_back进去的,所以尾地址-1就好了
if(freq[min_freq].size() == 0) 代码走到这一步,一定是有key存在的。如果min_freq没有了值,证明就是
++min_freq;
return hash[key].first;
} void put(int key, int value) {
if(cap <= 0)
return;
if(get(key) != -1){ 注意调用的是 get(key),不是find
hash[key].first = value; 如果这个key已经存在,就不用考虑容量的问题,直接更新value就好了,因为不用新添加
return;
}
if(hash.size() >= cap){
hash.erase(freq[min_freq].front()); 删除出现次数最少的中最久未出现的,list中越新出现的从后push进去
iter.erase(freq[min_freq].front()); 为什么要删除迭代器???
freq[min_freq].pop_front();
}
hash[key] = {value,1}; 对出现一次的key进行添加
freq[1].push_back(key);
iter[key] = --freq[1].end();
min_freq = 1;
}
int cap,min_freq;
unordered_map<int,pair<int,int>> hash; <key,pair<value,freq>>
unordered_map<int,list<int>> freq; <freq,list<key>>
unordered_map<int,list<int>::iterator> iter; <freq,list<key>::iterator>
};
leetcode 146. LRU Cache 、460. LFU Cache的更多相关文章
- [LeetCode] 460. LFU Cache 最近最不常用页面置换缓存器
Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the f ...
- [LeetCode] 146. LRU Cache 最近最少使用页面置换缓存器
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...
- [LeetCode] 146. LRU Cache 近期最少使用缓存
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...
- LeetCode 146. LRU缓存机制(LRU Cache)
题目描述 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key) - 如果密钥 (k ...
- [Leetcode]146.LRU缓存机制
Leetcode难题,题目为: 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key ...
- Java实现 LeetCode 146 LRU缓存机制
146. LRU缓存机制 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key) - ...
- Java for LeetCode 146 LRU Cache 【HARD】
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...
- leetcode 146. LRU Cache ----- java
esign and implement a data structure for Least Recently Used (LRU) cache. It should support the foll ...
- leetcode@ [146] LRU Cache (TreeMap)
https://leetcode.com/problems/lru-cache/ Design and implement a data structure for Least Recently Us ...
随机推荐
- 文件的空间使用和IO统计
数据库占用的存储空间,从高层次来看,可以查看数据库文件(数据文件,日志文件)占用的存储空间,从较细的粒度上来看,分为数据表,索引,分区占用的存储空间.监控数据库对象占用的硬盘空间,包括已分配,未分配, ...
- element之tree组件样式重写
1.改写实现效果: 2.页面代码 <el-tree :data="data" :props="defaultProps" @node-click=&quo ...
- RobHess的SIFT代码解析步骤一
平台:win10 x64 +VS 2015专业版 +opencv-2.4.11 + gtk_-bundle_2.24.10_win32 主要参考:1.代码:RobHess的SIFT源码:SIFT+KD ...
- Delphi 数组特性
- Django—logging日志
简介 Django使用python自带的logging 作为日志打印工具.简单介绍下logging. logging 是线程安全的,其主要由4部分组成: Logger 用户使用的直接接口,将日志传递给 ...
- 12_Hive实战案例_累计报表_级联求和
注:Hive面试题:累积报表 数据文件: 有如下访客访问次数统计表 t_access_times 需要输出报表:t_access_times_accumulate 实现步骤: 创建表,并将数据加载到表 ...
- 5.caffe图片分类流程
一次创建下列文件: 1,create_txt.sh (create_filelist.sh) 2,create_lmdb.sh 3,make_mean.sh 4,train.prototxt+val. ...
- Java工程师面试题集锦
即将踏上找工作的征途,参考网上面试题库准备一波面试题,希望能找到理想中的工作,愿一切顺利. 一.Java基础 1.String类为什么是final的. 2.HashMap的源码,实现原理,底层结构. ...
- jQuery超酷响应式瀑布流效果
参考 http://www.sucaihuo.com/js/74.html <script src="scripts/blocksit.min.js"></scr ...
- IDEA-maven的配置
一.下载maven的包 http://www.apache.org/ 1.在网页中打开上面的网址,进入下面的页面 2.拖动滚动条往下拉,找到maven 进入之后,点击Download 3.选择wind ...