刷题65— LFU缓存

102.LFU缓存

题目链接
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lfu-cache

题目描述
设计并实现最不经常使用(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

重难点
cache数组类型为{value, freq}[]
重点在根据freq更新列表顺序上, 这里使用了findIndex的数组原生方法, 找到第一个小于等于该item.freq的index, 插入即可.

作者:jiang-li-rui
链接:https://leetcode-cn.com/problems/lfu-cache/solution/yuan-sheng-shu-zu-fei-o1ban-ben-by-jiang-li-rui/

题目分析
这道题我真不会,看了解析以后,嗯,还是困难,查了相关知识点,努力理解。
cache数组类型为{value, freq}[]
重点在根据freq更新列表顺序上, 这里使用了findIndex的数组原生方法, 找到第一个小于等于该item.freq的index, 插入即可。

/**
 * @param {number} capacity
 */
var LFUCache = function(capacity) {
    this.cache = [];
    this.L = capacity;
};

/** 
 * @param {number} key
 * @return {number}
 */
LFUCache.prototype.get = function(key,value) {
    //查找key相同的值
    let index = this.cache.findIndex(item=>item.key === key);
    if(index !== -1){
        //如果有值
        const item = this.cache[index];
        //如果值有更改
        if(value) item.value = value;
        //调用更新函数
        this.update(item,index); 
        // 返回该值
        return item.value;
    }
    return -1;
};
/** 
 * 更新策略
 * @param {array} item 待更新的元素
 * @param {number} idx 索引
 * @return {void}
 */
LFUCache.prototype.update = function(item, idx) {
    // 如果idx存在, 也就是获取而非添加的情况
    if(idx !== undefined){
        // 增加其使用频率
        item.freq += 1
        // 从原数组中删除, 在后续过程中在添加到预定位置
        this.cache.splice(idx, 1)
    }
    // 获取要插入的位置索引, 找到第一个freq小于等于待插入元素freq的地方
    let index = this.cache.findIndex((a, i) => a.freq <= item.freq)
    // 如果没有找到, 证明所有元素都大于待插入元素, 所以在末尾添加
    if(index === -1) {
        index = this.cache.length
    }
    // 在index出添加待插入元素
    this.cache.splice(index, 0, item)
};
/** 
 * @param {number} key 
 * @param {number} value
 * @return {void}
 */
LFUCache.prototype.put = function(key, value) {
    //长度为0,return 0
    if(this.L === 0) return 0;
    //如果没有获取值
    if(this.get(key, value) === -1){
        // 如果长度到了限制
        if(this.cache.length === this.L) {
            // 删除最末尾一个(肯定为使用频率最少的)
            this.cache.pop()
        }
        // 插入元素
        this.update({key, value,freq: 0})
    }
};

/**
 * Your LFUCache object will be instantiated and called as such:
 * var obj = new LFUCache(capacity)
 * var param_1 = obj.get(key)
 * obj.put(key,value)
 */
``` class LFUCache { private: int capacity; int minFreq; unordered_map<int, pair<int, int>> cache; // key -> {value, freq} unordered_map<int, list<int>::iterator> freqMap; // freq -> iterator of keys unordered_map<int, list<int>> lruMap; // freq -> list of keys public: LFUCache(int capacity) { this->capacity = capacity; minFreq = 0; } int get(int key) { if (cache.count(key) == 0) { return -1; } int value = cache[key].first; int freq = cache[key].second; freqMap.erase(key); lruMap[freq].erase(freqMap[key]); if (lruMap[freq].empty()) { lruMap.erase(freq); if (freq == minFreq) { minFreq++; } } freq++; cache[key] = {value, freq}; lruMap[freq].push_front(key); freqMap[key] = lruMap[freq].begin(); return value; } void put(int key, int value) { if (capacity == 0) { return; } if (cache.count(key) != 0) { cache[key].first = value; get(key); return; } if (cache.size() == capacity) { int keyToRemove = lruMap[minFreq].back(); lruMap[minFreq].pop_back(); freqMap.erase(keyToRemove); cache.erase(keyToRemove); } minFreq = 1; cache[key] = {value, minFreq}; lruMap[minFreq].push_front(key); freqMap[key] = lruMap[minFreq].begin(); } }; ``` LFU Cache的实现使用了三个哈希表: - `cache` 哈希表:存储键值对和对应的频率。 - `freqMap` 哈希表:存储键值对的频率和对应的迭代器。 - `lruMap` 哈希表:存储键值对的频率和对应的链表。 其中,`cache` 哈希表用于快速查找键值对,`freqMap` 哈希表用于快速查找键值对对应的迭代器,`lruMap` 哈希表用于快速查找某个频率对应的键值对。 LFU Cache有两个方法: - `get` 方法:根据键获取值,如果键不存在则返回 -1。同时更新键值对的频率和链表。 - `put` 方法:插入一个键值对。如果键已存在,则更新值和频率;如果容量已满,则删除频率最小的键值对。同时更新最小频率和链表。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值