leetcode 146. LRU Cache 、460. LFU Cache

时间:2022-07-04 23:33:13

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;
}

测试的结果:

leetcode 146. LRU Cache 、460. LFU Cache

在测试的时候,自己对构造函数的初始化不是很了解,所以最开始的测试代码错误写成如下:

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连接

leetcode 146. LRU Cache 、460. LFU Cache
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 146. LRU Cache 、460. LFU Cache的更多相关文章

  1. &lbrack;LeetCode&rsqb; 460&period; LFU Cache 最近最不常用页面置换缓存器

    Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the f ...

  2. &lbrack;LeetCode&rsqb; 146&period; LRU Cache 最近最少使用页面置换缓存器

    Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...

  3. &lbrack;LeetCode&rsqb; 146&period; LRU Cache 近期最少使用缓存

    Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...

  4. LeetCode 146&period; LRU缓存机制(LRU Cache)

    题目描述 运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key) - 如果密钥 (k ...

  5. &lbrack;Leetcode&rsqb;146&period;LRU缓存机制

    Leetcode难题,题目为: 运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key ...

  6. Java实现 LeetCode 146 LRU缓存机制

    146. LRU缓存机制 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制.它应该支持以下操作: 获取数据 get 和 写入数据 put . 获取数据 get(key) - ...

  7. Java for LeetCode 146 LRU Cache 【HARD】

    Design and implement a data structure for Least Recently Used (LRU) cache. It should support the fol ...

  8. leetcode 146&period; LRU Cache ----- java

    esign and implement a data structure for Least Recently Used (LRU) cache. It should support the foll ...

  9. leetcode&commat; &lbrack;146&rsqb; LRU Cache &lpar;TreeMap&rpar;

    https://leetcode.com/problems/lru-cache/ Design and implement a data structure for Least Recently Us ...

随机推荐

  1. 使用Azure Blob存储

    可以通过多种方式来对Azure Blob进行操作.在此我们介绍通过VS的客户端及代码两种方式来操作Blob. 一.通过VS来操作Blob. 1.首先下载publish settings 文件:打开“h ...

  2. 译:泛型List集合转化为DateTable的扩展方法

    译文出处:http://www.codeproject.com/Tips/867866/Extension-Method-for-Generic-List-Collection-to-Da 这段代码是 ...

  3. PKCS &num;1 RSA Encryption Version 1&period;5

    PKCS #1  RSA Encryption Version 1.5 在进行RSA运算时需要将源数据D转化为Encryption block(EB).其中pkcs1padding V1.5的填充模式 ...

  4. python 【第三篇】:函数及参数

    函数背景 在学习函数之前,一直遵循:面向过程编程: 根据业务逻辑从上到下实现功能,其往往用一长段代码来实现指定功能,开发过程中最常见的操作就是粘贴复制,也就是将之前实现的代码块复制到现需功能处,如下: ...

  5. linux中如何修改文件夹的用户权限 chown命令

    linux中,可以使用chown命令来修改文件夹的用户权限. 1.  以普通用户 A 登录linux,利用su -切换到root用户 2. 在root用户下,可以看到文件夹内容 3. 但通过文件系统, ...

  6. SQL——嵌套查询与子查询

    前言 sql的嵌套查询可以说是sql语句中比较复杂的一部分,但是掌握好了的话就可以提高查询效率.下面将介绍带in的子查询.带比较运算符的子查询.带any/all的子查询.带exists的子查询以及基于 ...

  7. 安装pitchpork 及 pacbioscience 的问题及解决

    1. error while loading shared libraries: libpbbam.so: cannot open shared 解决: find -name  libpbbam.so ...

  8. webapi postman 415 错误

    https://blog.csdn.net/Intangible_moon/article/details/80183121 猜测:如果后台接口使用的是[fromBody]标签的话,需要使用raw方式

  9. IIS7&period;0 下使用Intelligencia&period;UrlRewriter时Session为空问题

    背景 新年伊始,本人的开发环境由Windows Server 2003 +IIS 6 升级成了 Windows Server 2008 +IIS 7,之后便着手参加新项目的开发.项目开发后期测试过程中 ...

  10. 《Effective Java》笔记-服务提供者框架

    静态工厂方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不必存在.这种灵活的静态工厂方法构成了服务提供者框架(Service Provider Framework)的基础,例如JDBC AP ...