LFU缓存

题目:请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。它应该支持以下操作:get 和 put。
    get(key) - 如果键存在于缓存中,则获取键的值(总是正数),否则返回 -1。
    put(key, value) - 如果键不存在,请设置或插入值。当缓存达到其容量时,则应该在插入新项之前,使最不经常使用的项无效。

            在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最近 最少使用的键。
    「项的使用次数」就是自插入该项以来对其调用 get 和 put 函数的次数之和。使用次数会在对应项被移除后置为 0 。

示例:  
   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

代码:

 class LFUCache {

     public LFUCache(int capacity) {

     }

     public int get(int key) {

     }

     public void put(int key, int value) {

     }
} /**
* 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);
*/

LFU页面置换算法(最不经常使用算法)

  原理:

    选择到当前时间为止被访问次数最少的页面被置换;
    每页设置访问计数器,每当页面被访问时,该页面的访问计数器加1;
    发生缺页中断时,淘汰计数值最小的页面,并将所有计数清零;

    如图:图中的页面为三页,依次向存储中加入432143543215这些数字。

       而存储空间只能存储三个页面,所以会按照上述规则不断淘汰已经存储在页面中的数字。

    

解题思路(logN的思路):

    知道了LFU的置换规则后,由于此题需要存储的是key和value,所以

      首先,需要建一个类node,存放四样东西,key,value,times(访问计数器),id(进入存储空间的自然顺序)

      其次,选择一种合适的数据结构来解决存储优先级问题,此处我们采用内部是小顶堆的PriorityQueue优先级队列用来

         实现times最小的元素在队头,如果times相等,则比较先后入队的自然顺序id

         但是我们会在让新元素入队之前可能会删除队列中指定元素,当然可以去遍历队列,但是这样太慢了

         我们可以再用一种HashMap的数据集合用来存储节点,以便快速通过node的key来得到整个node。

      最后,便是处理逻辑关系,写题目要求的get,put方法了

解题代码详解(logN):

 public class node implements Comparable<node>{
private int Key;//键
private int Value;//值
private int Times;//访问计数器
private int Id;//自然入队顺序标记,若访问计数器值相同,则先淘汰id小的那个
node() {}
node(int key, int value, int id) {
this.Key = key;
this.Value = value;
this.Id = id;
this.Times = 1;
}
public int getKey() {
return Key;
} public void setKey(int Key) {
this.Key = Key;
} public int getValue() {
return Value;
} public void setValue(int Value) {
this.Value = Value;
} public int getTimes() {
return Times;
} public void setTimes(int Times) {
this.Times = Times;
}
public int getId() {
return Id;
} public void setId(int id) {
this.Id = id;
} @Override
public int compareTo(node o) {
//实现times最小的元素在队头,如果times相等,则比较先后入队顺序
int Timessub = Times - o.Times;
return Timessub == 0 ? this.Id - o.Id: Timessub;
}
} class LFUCache {
PriorityQueue<node> KeyValueTimes = new PriorityQueue();//用于实现优先级顺序
Map<Integer, node> nodeset;//用于O(1)取出某个具体的node
public int Capacity = 0;//我的cache中最大容量
public int nownum = 0;//cache的实时元素个数
public int id = 0;//每个node的入队自然顺序标记 public LFUCache(int capacity) {
this.Capacity = capacity;//设置cache容量
nodeset = new HashMap<Integer, node>(capacity);//用于O(1)取出某个具体的node,容量依然设置为capacity
} public int get(int key) {
if(this.Capacity == 0)//判断容量是否为空,为空则直接返回-1
return -1;
node nownode = nodeset.get(key);//通过HashMap,快速通过key键快速得到node
if (nownode == null) {//如果key这个键没在队列中,则返回-1
return -1;
}else{
KeyValueTimes.remove(nownode);//移除队列中当前的这个node
nownode.setTimes(nownode.getTimes()+1);//更新当前这个node的访问次数
nownode.setId(id++);//更新自然入队顺序
KeyValueTimes.offer(nownode);//再把它放回去
}
return nownode.getValue();
} public void put(int key, int value) {
if(this.Capacity == 0)//判断容量是否为空,为空则不进行put
return;
node thisnode = new node(key,value,id++);
node oldnode = nodeset.get(key);
if(oldnode == null){//队列里不存在这个key
if(nownum < this.Capacity){//没装满
KeyValueTimes.offer(thisnode);//在队列里添加新node
nodeset.put(key,thisnode);//在HashMap里添加新node
nownum++;//更新当前cache的元素个数
}
else{//装满了,需要LFU,最不经常使用被移除
nodeset.remove(KeyValueTimes.poll().getKey());//移除队列里的队头,移除HashMap对应的那个node
KeyValueTimes.offer(thisnode);//在队列里添加新node
nodeset.put(key,thisnode);//在HashMap里添加新node
}
}
else{//队列里存在这个key
thisnode.setTimes(oldnode.getTimes()+1);//将原来键为key的访问次数复制给新的node
KeyValueTimes.remove(oldnode);//移除队列里键为key的node,移除HashMap对应的那个node
nodeset.remove(oldnode.getKey());
KeyValueTimes.offer(thisnode);//在队列里添加新node,这里新的node的value值可能会不一样,所以更新了value
nodeset.put(key,thisnode);//在队列里添加新node,这里新的node的value值可能会不一样,所以更新了value
}
}
}

操作系统-1-存储管理之LFU页面置换算法(leetcode460)的更多相关文章

  1. 操作系统笔记(六)页面置换算法 FIFO法 LRU最近最久未使用法 CLOCK法 二次机会法

    前篇在此: 操作系统笔记(五) 虚拟内存,覆盖和交换技术 操作系统 笔记(三)计算机体系结构,地址空间.连续内存分配(四)非连续内存分配:分段,分页 内容不多,就不做index了. 功能:当缺页中断发 ...

  2. [Operate System & Algorithm] 页面置换算法

    页面置换算法是什么?我们看一下百度百科对页面置换算法给出的定义:在地址映射过程中,若在页面中发现所要访问的页面不在内存中,则产生缺页中断.当发生缺页中断时,如果操作系统内存中没有空闲页面,则操作系统必 ...

  3. 页面置换算法(最佳置换算法、FIFO置换算法、LRU置换算法、LFU置换算法)

    页面置换产生的原因是:分页请求式存储管理(它是实现虚拟存储管理的方法之一,其中一个特性是多次性-->多次将页面换入或换出内存) 效果最好的页面置换算法:最佳置换算法 比较常用的页面置换算法有:F ...

  4. 操作系统页面置换算法(opt,lru,fifo,clock)实现

    选择调出页面的算法就称为页面置换算法.好的页面置换算法应有较低的页面更换频率,也就是说,应将以后不会再访问或者以后较长时间内不会再访问的页面先调出. 常见的置换算法有以下四种(以下来自操作系统课本). ...

  5. 操作系统 页面置换算法LRU和FIFO

    LRU(Least Recently Used)最少使用页面置换算法,顾名思义,就是替换掉最少使用的页面. FIFO(first in first out,先进先出)页面置换算法,这是的最早出现的置换 ...

  6. 页面置换算法 - FIFO、LFU、LRU

    缓存算法(页面置换算法)-FIFO. LFU. LRU 在前一篇文章中通过leetcode的一道题目了解了LRU算法的具体设计思路,下面继续来探讨一下另外两种常见的Cache算法:FIFO. LFU ...

  7. 操作系统-2-存储管理之LRU页面置换算法(LeetCode146)

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

  8. 【操作系统】页面置换算法(最佳置换算法)(C语言实现)

    [操作系统]页面置换算法(最佳置换算法)(C语言实现) (编码水平较菜,写博客也只是为了个人知识的总结和督促自己学习,如果有错误,希望可以指出) 1.页面置换算法: 在地址映射过程中,若在页面中发现所 ...

  9. (待续)C#语言中的动态数组(ArrayList)模拟常用页面置换算法(FIFO、LRU、Optimal)

    目录 00 简介 01 算法概述 02 公用方法与变量解释 03 先进先出置换算法(FIFO) 04 最近最久未使用(LRU)算法 05 最佳置换算法(OPT) 00 简介 页面置换算法主要是记录内存 ...

随机推荐

  1. Json转化的三种方式

    1. Gson 1.添加依赖 <dependency> <groupId>com.google.code.gson</groupId> <artifactId ...

  2. 使用增量备份修复DG中的GAP

    问题描述 oracle中DG出现主备不同步现象,alert日志报警有gap信息,但是v$archive_gap视图查不到任何信息.同时主库上的对应归档已经删除且没有备份 解决方案 1.查询备库的scn ...

  3. 内网渗透之跨边界传输 - LCX转发

    跨边界转发 端口转发 lcx 流程 目标机 ./lcx -slave 跳板机ip 监听的端口 127.0.0.1 要转发的端口 跳板机(公网) ./lcx -listen 监听的端口 转发给攻击机访问 ...

  4. 在Deepin系统上装Python 3.8遇到的那些坑

    - 作为一天时间在Deepin上都没装好Python的代表,我感觉有必要记录一下我自己的解决方法 坑1-- SSL/TLS 字样错误 "pip is configured wih locat ...

  5. 简说Python之IO

    闺女,你在玩电脑什么游戏? 爸爸,我在玩植物大战僵尸呢. 闺女,你知道什么是输入输出设备吗? 爸爸,??? 你看,咱们的键盘和鼠标 ,就是可以控制那些植物的工具.这些发出指令的就是输入设备.咱们可以用 ...

  6. DBProxy快速入门

    1. DBProxy安装 1.1 安装依赖项 CentOS yum install -y Percona-Server-devel-55.x86_64 Percona-Server-client-55 ...

  7. Django中update和save()同时作用

    数据更新操作,对单条记录,可以使用save或者是update两种方式. save() 默认保存后会看到sql语句中更新了所有字段,而save的值是之前获取时候的字段值,是缓存下来的,并不一定最新,可能 ...

  8. LeetCode(239.滑动窗口的最大值

    题目: 给定一个数组nums,有一个大小为k的滑动窗口从数组的最左侧移动到最右侧,你只可以看到滑动窗口内的k个数字.滑动窗口每次只向右移动一位. 返回滑动窗口中的最大值. 示例: 输入: nums = ...

  9. java实现QQ、微信、轰炸机,撩妹,抖图功能,轻松自如

    今天交大家一个很牛的功能,让你朋友服你,他不扶你你来找我. 打游戏被骂,骂不过你来找我,我们有神器,直到他怕了为止. 废话少说,代码如下,动手,干就完了 乞丐版如下 参考连接:Java实现QQ微信轰炸 ...

  10. 最长公共前缀(py)

    编写一个函数来查找字符串数组中的最长公共前缀. 如果不存在公共前缀,返回空字符串 "". 示例 1: 输入: ["flower","flow" ...