题解 | #设计LFU缓存结构,双hash表+链表#

设计LFU缓存结构

http://www.nowcoder.com/practice/93aacb4a887b46d897b00823f30bfea1

import java.util.*;


class Node {//节点(数据单元)
    int key ;
    int val ;
    int count ;//节点访问的次数
    Node(int key , int val) {
        this.key = key ;
        this.val = val ;
    }
}
class LFU {//缓存结构
    //根据key查询Node
    HashMap<Integer , Node> dic ;
    /*根据访问次数count查询List(list里面存储所有访问次数为count的节点,并且约靠近链尾的节点约久未访问)*/
    HashMap<Integer , LinkedList<Node>> tim ;
    int minTim ;//被访问最少的节点的 访问次数
    int maxSize ;//LFU缓存的容量
    int size ;//当前使用的容量
    LFU(int capcity) {
        this.dic = new HashMap<>() ;
        this.tim = new HashMap<>() ;
        this.minTim = 0 ;  
        this.maxSize = capcity ;
        this.size = 0 ;
    }
    //set(key,value)
    public void set(int key , int val) {
        if(dic.containsKey(key)) {//如果缓存中存在key
            Node find = dic.get(key) ;
            find.val = val ;//置换key所对应的value
            flush(find) ;//刷新一下访问记录,即让当前的访问次数加一,并且从原来的链表移动到新的链表
        } else {//如果缓存中不存在key
            Node newNode = new Node(key , val) ;//新建节点
            newNode.count = 1 ;//第一次访问
            if(this.size == this.maxSize) {//如果缓存满了
                removeByLFU() ;//置换        
            } else {//缓存未满,size加一
                this.size ++ ;
            }
            dic.put(key , newNode) ;//存数据
            if(tim.containsKey(1)) {//如果已经存在次数为1的链表
                tim.get(1).addFirst(newNode) ;//直接添加,在链头哦
            } else {//如果不存在次数为1的链表
                LinkedList<Node> newList = new LinkedList<>() ;//新建一个链表
                newList.add(newNode) ;//添加
                this.minTim = 1 ;//刷新最小访问次数
                tim.put(1 , newList) ;//将该链表以key为1,存入tim
            }
        }
    }
    //get(key)
    public int get(int key) {
        if(!dic.containsKey(key)) return -1 ;//不存在直接返回-1
        Node find = dic.get(key) ;//得到对应的节点
        flush(find) ;//刷新节点的访问记录
        return find.val ;
    }
    //刷新节点的访问记录
    public void flush(Node find) {
        LinkedList<Node> oldBelongsList = tim.get(find.count) ;//获取节点原来所在的链表
        oldBelongsList.remove(find) ;//从原来的链表中移除
        if(oldBelongsList.size() == 0) {//如果原来的链表已经没有数据了
            if(find.count == this.minTim) {//并且 当前节点就是最少访问节点,那么要更新最少访问节点的访问次数
                this.minTim ++ ;    
            }
            tim.remove(find.count) ;//把空链表移除
        }
        find.count ++ ;//当前节点的访问次数+1
        if(tim.containsKey(find.count)) {//如果新的访问次数岁对应的链表已经存在
            tim.get(find.count).addFirst(find) ;//则直接在新的链表中加入当前节点
        } else {//如果新的访问次数岁对应的链表不存在
            LinkedList<Node> newBelongsList = new LinkedList<>() ;//创建链表
            newBelongsList.add(find) ;//添加节点
            tim.put(find.count , newBelongsList) ;//将新的链表加入到tim中
        }
    }
    //(淘汰访问次数最少的节点,次数一样则淘汰最久未访问的节点)
    public void removeByLFU() {
        LinkedList<Node> minTimList = tim.get(this.minTim) ;//获取最少访问次数所对应的链表
        Node target = minTimList.get(minTimList.size()-1) ;//获取这个访问次数的"众多"节点中的最久未访问哪一个节点(即,链表尾部的那个节点)
        minTimList.remove(target) ;//删除那个节点
        if(minTimList.size() == 0) {//如果这个链表已经空了,则将其从tim中移除掉
            tim.remove(this.minTim) ;
        }
        dic.remove(target.key) ;//将节点从dic中移除
        //注意:这个方法中并没有更新size和minTim,因为客户在调用本方法后,必定会再增加一个节点
        //然后将minTim设置为1,(所以在本方法更新size和minTim多余,至少在这种实现的方式下是的,嘿嘿,才疏学浅,见谅!!)
    }
}
public class Solution {
    /**
     * lfu design
     * @param operators int整型二维数组 ops
     * @param k int整型 the k
     * @return int整型一维数组
     */
    public int[] LFU (int[][] operators, int k) {
        int getCount = (int)(Arrays.stream(operators).filter(a->a[0] == 2).count()) ;
        int res[] = new int[getCount] ;
        int index = 0 ;
        LFU lfu = new LFU(k) ;
        for(int i = 0 ; i < operators.length ; i ++) {
            if(operators[i].length == 2) {
                res[index ++] = lfu.get(operators[i][1]) ;
            } else {
                lfu.set(operators[i][1] , operators[i][2]) ;
            }
        }
        return res ;
    }
}

一个菜鸟的算法刷题记录 文章被收录于专栏

分享一个菜鸟的成长记录

全部评论

相关推荐

大方的大熊猫准备进厂:1.教育背景:你希望从事什么专业的工作你的主修课就是什么;成绩优秀是你应该做的,没什么可描述的,成绩不优秀也许人家在大学忙着创业呢?(成绩优秀不一定是好事,只能说明多元化的大学你上成了高中,没有真正上明白大学,反而体现了你死板,不爱社交,没有别的突出能力) 2.实践经历:你想表达的意思没有说清楚。你是说你会个性化服务,还是你有实习经历。如果没有带来,经济收益,表彰,更好的发展前景,那你还不如说说提升了自己哪些技能。你说有人给你送锦旗我都能明白你优秀,但是你说你会xxxx,你说这话谁信,证据呢。 3.入伍经历:你描述的就是你的工作职责或者你应该做的,并没有体现出来你把这个事情做好了,而且入伍经历并不能证明你能干好你要应聘的工作,不如只写经历其余所有内容都不写。 4.荣誉技能:重点突出一下,但不要过多描述,这些荣誉的含金量懂得都懂。 重点:你要应聘什么工作(具体岗位,实习生不具体),你的期望薪资
点赞 评论 收藏
分享
葬爱~冷少:我当时都是上午刷力扣,下午背八股,有活给我先别急,没活就干自己的事情
点赞 评论 收藏
分享
评论
2
2
分享

创作者周榜

更多
牛客网
牛客企业服务