【题目描述】

设计并实现最不经常使用(LFU)缓存的数据结构。它应该支持以下操作:get 和 put。

get(key) - 如果键存在于缓存中,则获取键的值(总是正数),否则返回 -1。

put(key, value) - 如果键不存在,请设置或插入值。当缓存达到其容量时,它应该在插入新项目之前,使最不经常使用的项目无效。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,最近最少使用的键将被去除。

进阶:

你是否可以在 O(1) 时间复杂度内执行两项操作?

示例:

LFUCache cache = new LFUCache( 2 /*capacity (缓存容量) */ );

cache.put(1, 1);

cache.put(2, 2);

cache.get(1); // 返回 1

cache.put(3, 3); // 去除 key 2

cache.get(2); // 返回 -1 (未找到key 2)

cache.get(3); // 返回 3

cache.put(4, 4); // 去除 key 1

cache.get(1); // 返回 -1 (未找到 key 1)

cache.get(3); // 返回 3

cache.get(4); // 返回 4

【解题思路】

解题思路见我博客:左神算法进阶班6_1LFU缓存实现

【代码实现】

  

 #pragma once
#include <iostream>
#include <map> using namespace std; class LFUCache {
public:
LFUCache(int capacity) {
this->capacity = capacity;
} int get(int key) {
if (dataMap.find(key) == dataMap.end())//数据不存在
return -;
Node* p = dataMap[key];//找到数据节点
NodeList* h = headMap[p->num];
updataNode(p, h); return p->val;
} void put(int key, int value) {
if (capacity == )
return;
if (dataMap.find(key) != dataMap.end())//已经存在
{
Node* p = dataMap[key];//找到数据节点
NodeList* h = headMap[p->num];//找到头链表节点
p->val = value; updataNode(p, h);//更新数据的使用次数
}
else//如果不存在,则新建
{
if (dataMap.size() >= this->capacity)//容量不足,需要删除数据
deleteData(); Node* p = new Node(key, value, );//使用用一次
dataMap[key] = p;//记录 //将新建节点插入使用1次的子链表中
if (headMap.find() == headMap.end())//当使用1次的子链表不存在
createNode(p, headList);
else
moveNode(p, headMap[]);//插入在使用次数在1的子链表中
}
} private:
struct Node//子链表
{
int key;
int val;
int num;
Node* next;
Node* pre;
Node(int a, int b, int n) :key(a), val(b), num(n), next(nullptr), pre(nullptr) {}
}; struct NodeList//主链表
{
int num;
Node* head;//子链表的头节点
Node* tail;//子链表的尾结点
NodeList* next;
NodeList* pre;
NodeList(int a) :num(a), next(nullptr), pre(nullptr)
{
head = new Node(, , a);//新建一个子链表的头结点
tail = head;
}
}; private:
void getNode(Node*& p, NodeList*& h)//将节点从子链表中取出
{
p->pre->next = p->next;
if (p->next == nullptr)
h->tail = p->pre;
else
p->next->pre = p->pre;
}
void moveNode(Node*& p, NodeList*& q)//将节点向后移动
{
p->next = q->tail->next;
q->tail->next = p;
p->pre = q->tail;
q->tail = p;
}
void deleteNode(int num, NodeList*& h)//删除子链表
{
headMap.erase(num);//从map中删除
h->pre->next = h->next;
if (h->next != nullptr)
h->next->pre = h->pre;
delete h;
h = nullptr;
}
void createNode(Node*p, NodeList*& h)//新建子链表,并插入在主链中
{
NodeList* q = new NodeList(p->num);//新建一个子链表
headMap[p->num] = q;//保存对应的地址 moveNode(p, q);////将节点放入子链表中 //将新子链插入主链表中
q->next = h->next;
if (h->next != nullptr)
h->next->pre = q;
h->next = q;
q->pre = h;
}
void updataNode(Node*& p, NodeList*& h)//更新函数的使用次数
{
int num = p->num;
p->num++;//使用次数+1 //将p从子链表中取出
getNode(p, h); //将该数据向后面移动
if (headMap.find(p->num) == headMap.end())//不存在num+1的节点,那么新建
createNode(p, h);
else
moveNode(p, headMap[p->num]);////将节点放入子链表中 //如果该子链表为空,将该子链表删除,并从map中删除
if (h->head == h->tail)
deleteNode(num, h);
}
void deleteData()//容量不足需要删除
{
NodeList* p = headList->next;
Node* q = p->head->next;//删除子链表排在最前面的数据
if (q == p->tail)//要删除的数据就是最后一个数据,则删除该节点和子链表
deleteNode(q->num, p);
else
{
p->head->next = q->next;
q->next->pre = p->head;
}
dataMap.erase(q->key);//删除记录
delete q;//删除
q = nullptr;
} private:
int capacity;
NodeList* headList = new NodeList();//主链表的头结点
map<int, Node*>dataMap;//key <——> 真实数据节点地址
map<int, NodeList*>headMap;//次数 <——> 链表头节点地址
}; /**
* Your LFUCache object will be instantiated and called as such:
* LFUCache* obj = new LFUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/ void Test()
{
LFUCache* f = new LFUCache();
f->put(, );
f->put(, );
f->put(, );
f->put(, );
cout << f->get() << endl;
cout << f->get() << endl;
cout << f->get() << endl;
cout << f->get() << endl;
f->put(, );
cout << f->get() << endl;
cout << f->get() << endl;
cout << f->get() << endl;
cout << f->get() << endl;
cout << f->get() << endl; }

力扣算法题—460LFU缓存的更多相关文章

  1. 力扣算法题—146LRU缓存机制

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

  2. 力扣算法题—069x的平方根

    实现 int sqrt(int x) 函数. 计算并返回 x 的平方根,其中 x 是非负整数. 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去. 示例 1: 输入: 4 输出: 2 示例 ...

  3. 力扣算法题—060第K个排列

    给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列. 按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下: "123" "132&qu ...

  4. 力扣算法题—050计算pow(x, n)

    #include "000库函数.h" //使用折半算法 牛逼算法 class Solution { public: double myPow(double x, int n) { ...

  5. 力扣算法题—147Insertion_Sort_List

    Sort a linked list using insertion sort. A graphical example of insertion sort. The partial sorted l ...

  6. 力扣算法题—093复原IP地址

    给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式. 示例: 输入: "25525511135" 输出: ["255.255.11.135", ...

  7. 力扣算法题—079单词搜索【DFS】

    给定一个二维网格和一个单词,找出该单词是否存在于网格中. 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格.同一个单元格内的字母不允许被重复使用. ...

  8. 力扣算法题—052N皇后问题2

    跟前面的N皇后问题没区别,还更简单 #include "000库函数.h" //使用回溯法 class Solution { public: int totalNQueens(in ...

  9. 力扣算法题—051N皇后问题

    #include "000库函数.h" //使用回溯法来计算 //经典解法为回溯递归,一层一层的向下扫描,需要用到一个pos数组, //其中pos[i]表示第i行皇后的位置,初始化 ...

随机推荐

  1. nginx 知识

    nginx如何实现高并发? 启动nginx服务器后,输入 ps -ef |grep nginx,会发现nginx有一个master进程 和若干个worker进程, 这些worker进程是平等的,都是被 ...

  2. js添加节点

    <!DOCTYPE html><html><body><script>var message=document.createTextNode(" ...

  3. swagger.yaml转换为swagger.json文件

    方法一 swagger-editor页面 官方的 swagger-editor Live Demo (在线直接使用,就是访问的有点慢)或者将swagger-editor Download 下载到本地然 ...

  4. Erlang学习记录:运算符

    数学运算符 说明 详细 符号 加减乘 +-* 浮点数除 结果为浮点数 / 整数除 除数和被除数都必须是整数,结果为整数 div 整数取余 rem 逻辑运算符 说明 符号 详细 and 前后两个值都为真 ...

  5. Java基本语法.part02

    变量 变量的概念: 内存中的一个存储区域 该区域有自己的名称(变量名)和类型(数据类型) Java中每个变量必须先声明,后使用 该区域的数据可以在同一类型范围内不断变化 使用变量注意: 变量的作用域: ...

  6. windows下,根据端口号杀死进程

    1.cmd下,根据端口号查询进程号 C:\>netstat -ano|findstr " 2.根据进程号928,在任务管理器杀死PID为928进程 注意:默认情况下,进程选项卡里没有P ...

  7. 总结加密、机密jar中的class

    1.加密和解密部署到jboss中间件中的的单个class文件,原理:使用“java源程序加密解决方案(基于Classloader解密) (2014-07-13 11:31)”blog即可实现: imp ...

  8. Java反射简介

    Java反射简介 1.Class类 1) 在面向对象的世界里,万事万物皆对象.(java语言中,静态的成员.普通数据类型除外) 类是不是对象呢?类是(哪个类的对象呢?)谁的对象呢? 类是对象,类是ja ...

  9. Java lambda尝鲜

    最近Java 11都出来了,java 8 的很多新特性还没用过,比如接口可以有方法体啦,default默认方法. 当然最大的特性就是支持函数式编程了.Java 8 提供了lambda表达式和方法引用来 ...

  10. PostMan授权认证使用

    Authorization 对于很多应用,出于安全考虑我们的接口并不希望对外公开.这个时候就需要使用授权(Authorization)机制. 授权过程验证您是否具有访问服务器所需数据的权限. 当发送请 ...