leetcode 460. LFU Cache

本文详细介绍了如何设计和实现一种最少使用频率(LFU)缓存数据结构,该结构支持get和put操作,且能在缓存达到容量限制时,根据频率和最近使用情况淘汰元素。文章提供了完整的代码实现,包括使用双向链表和哈希映射来优化操作效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the following operations: get and put.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reaches its capacity, it should invalidate the least frequently used item before inserting a new item. For the purpose of this problem, when there is a tie (i.e., two or more keys that have the same frequency), the least recently used key would be evicted.

Follow up:
Could you do both operations in O(1) time complexity?

Example:

LFUCache cache = new LFUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
cache.get(2);       // returns -1 (not found)
cache.get(3);       // returns 3.
cache.put(4, 4);    // evicts key 1.
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4

 


====================2019.01.18==================

逻辑要清晰。更新频率map,再更新双向链表。

class LFUCache {
    class Node{
        int key;
        int val;
        int fre;
        Node pre;
        Node next;
        Node(int key,int val,int fre){
            this.key=key;
            this.val=val;
            this.fre=fre;
        }
    }
    HashMap<Integer,Node> fmap;
    HashMap<Integer,Node> map;
    Node first;
    Node end;
    final int K;
    int cap;
    public LFUCache(int capacity) {
        map=new HashMap<>();
        fmap=new HashMap<>();
        first=new Node(0,0,0);
        end=new Node(0,0,0);
        first.next=end;
        end.pre=first;
        K=capacity;
    }
    
    public int get(int key) {
        if(map.get(key)==null) return -1;
        Node node=map.get(key);
        int fre=node.fre;
        node.fre++;
        Node n=fmap.get(fre);//插入点
        //更新fmap
        if(fmap.get(fre)==node){
            if(node.pre.fre==fre) fmap.put(fre,node.pre);
            else fmap.remove(fre);
        }
        if(fmap.get(fre+1)!=null){
            n=fmap.get(fre+1);
        }
        fmap.put(fre+1,node);
        //更新双向链表
        if(node!=n){
            node.pre.next=node.next;
            node.next.pre=node.pre;
        
            node.next=n.next;
            node.pre=n;
            n.next.pre=node;
            n.next=node;    
        }
        return node.val;
    }
    
    public void put(int key, int value) {
        if(K==0) return;
        if(map.get(key)!=null){
            map.get(key).val=value;
            get(key);
        }else{
            Node node=new Node(key,value,1);
            //delete
            if(cap==K){
                Node n=first.next;
                if(fmap.get(n.fre)==n) fmap.remove(n.fre);
                map.remove(n.key);
                first.next=n.next;
                n.next.pre=first;
            }else{
                cap++;
            }
            //insert            
            if(fmap.get(1)==null){
                node.pre=first;
                node.next=first.next;
                first.next.pre=node;
                first.next=node;
            }else{
                Node n=fmap.get(1);
                node.pre=n;
                node.next=n.next;
                n.next.pre=node;
                n.next=node;
            }   
            fmap.put(1,node);
            map.put(key,node);
        }
    }
}

===============================================

总结:此题要注意的细节太多,借鉴了LRU那题,不同的是多用了了个HashMap保存了频率和该频率下最近用到的key。此题也暴露自己不够仔细的缺点,就是脑子里无法全面的设想各种情况。

class LFUCache {//找到频率最低的?只有是链表了,因为能快速找到下一个节点。。节点封装fre,value,最低频率
    HashMap<Integer,Node> fremap;//fre,node:每个频率最后一个节点,便于找到该插入的位置
    HashMap<Integer,Node> map;//key,node快速找到当前节点
    final private int capacity;
    int count=0;
    Node start=new Node(0,0,0);//链表起始节点
    class Node{
        int key;
        int val;
        int fre;
        Node next;
        Node pre;
        Node(int key,int val,int fre){
            this.key=key;
            this.val=val;
            this.fre=fre;
        }
    }
    public LFUCache(int capacity) {
        this.capacity=capacity;
        fremap=new HashMap<>();
        map=new HashMap<>();
    }
    
    public int get(int key) {
        if(capacity==0) return -1;
        //1.找得到
        //更新频率,并插入到链表合适位置,更新频率表
        //2.找不到
        //返回-1
        Node node=map.get(key);
        if(node!=null){//考虑起始节点,所以这里简化处理,创建起始节点用作标记节点
            Node last=fremap.get(node.fre);
            if(last.pre.fre!=last.fre){//该频率只有一个元素,就是当前元素
                //更新频率表
                fremap.remove(node.fre);
            }else if(last==node){//更新频率表!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!相等的时候更新!!!
                fremap.put(node.fre,last.pre);
            }
            if(fremap.get(node.fre+1)!=null){
                //原位置
                node.pre.next=node.next;
                node.next.pre=node.pre;
                //插入位置
                if(fremap.get(node.fre+1).next!=null) fremap.get(node.fre+1).next.pre=node;
                node.next=fremap.get(node.fre+1).next;
                node.pre=fremap.get(node.fre+1);
                fremap.get(node.fre+1).next=node;
            }else if(last!=node){//可能前面已清空或者
                node.pre.next=node.next;
                node.next.pre=node.pre;
                //插入位置
                if(last.next!=null) last.next.pre=node;
                node.next=last.next;
                node.pre=last;
                last.next=node;
            }
            //更新频率表
            node.fre++;
            fremap.put(node.fre,node);
        }else{
            return -1;
        }
        return node.val;
    }
    
    public void put(int key, int value) {
        Node n=start;
        if(capacity==0) return;
        if(map.get(key)!=null){
            map.get(key).val=value;
            get(key);//为了更新
            return;
        }  
        //插入位置
        Node node=new Node(key,value,1);
        map.put(key,node);
        //超出容量
        if(++count>capacity){
            //更新频率表
            if(fremap.get(start.next.fre)==start.next){
                fremap.remove(start.next.fre);
            }
            map.remove(start.next.key);
            start.next=start.next.next;
            if(start.next!=null) start.next.pre=start;
            count--;
        }
        //存在1
        if(fremap.get(1)!=null){//当插入新的节点时
            if(fremap.get(1).next!=null) fremap.get(1).next.pre=node;
            node.next=fremap.get(1).next;
            node.pre=fremap.get(1);
            fremap.get(1).next=node;
        }
        else{
            if(start.next!=null) start.next.pre=node;
            node.next=start.next;
            node.pre=start;
            start.next=node;
        }
        //更新频率表
        fremap.put(1,node);
    }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值