LeetCode:LRU and LFU

文章目录

  • [1. LRU](#1. LRU)
  • [2. LFU](#2. LFU)

1. LRU

Leetcode链接

LRU,即least recently used, 即一个具有固定容量的数据结构,其数据淘汰更新策略,根据最近访问,最久未被访问的元素优先被淘汰。

数据结构设计思路:

  • 链表。通过链表的尾部删除,头部插入,可以非常完美地模拟淘汰更新策略。
  • 哈希表。LRU访问,一般是KV结构,要能够通过key,找到value 。 实际中,哈希表建立的是key 到相应元素在链表中迭代器的映射关系 ,这样不仅可以拿到value,同时可以利用迭代器改变链表元素位置。
  • 整型变量。存储容量。
  • pair类型。存储KV键值对。
cpp 复制代码
class LRUCache {
public:
    LRUCache(int capacity) {
        capacity_ = capacity;
    }
    int get(int key) {
        if(hash_.count(key) == 0)
            return -1;
        auto it = hash_[key];
        int val = it->second;
        l_.erase(it);
        l_.push_front({key,val});
        hash_[key] = l_.begin();
        return val;
    } 
    
    void put(int key, int value) {
        if(hash_.count(key)) {
            hash_[key]->second = value;
            (void)get(key);
            return;
        }
        if(capacity_ == 0) {
            int key = l_.back().first;
            l_.pop_back();
            hash_.erase(key);
            capacity_++;
        }
        
        l_.push_front({key,value});
        hash_[key] = l_.begin();
        capacity_--;
    }
    
private:
    using pii = pair<int,int>;
    int capacity_;
    list<pii> l_;
    unordered_map<int,list<pii>::iterator> hash_;
};

2. LFU

Leetcode链接

LFU,即 least frequently used, 即最少频次被使用,同样是有一定淘汰更新策略的固定容量的数据结构。

LFU的淘汰更新策略:

  • 超出容量时,优先淘汰被使用频次最低的元素。
  • 相同频次的元素,优先淘汰最久未被使用的元素。

实际上,如果只看一个频次,那么LFU就是LRU,多个LRU分别对应不同频次,就构成了LFU。

所以,我们如下设计LFU数据结构:

  • 哈希表。这个哈希表建立的是频次到具体LRU链表的映射,这样可以通过频次,找到存储相应访问频次元素的链表。
  • 哈希表。该哈希表建立的是key到相应元素链表迭代器的映射,这样可以快速定位元素,而无需找到相应链表后,再遍历。
  • 整型变量。存储容量。
  • 整型变量。存储当前最小频次,这样淘汰时,可以快速通过哈希找到相应链表。
  • 元素数据结构。LRU 中,元素数据结构,简单的pair<int,int> 即可,而LFU 中,还需在 key 和 value 外添加频次,因为这样通过哈希拿到相应元素迭代器后,才能知道这个元素在哪个频次的链表中,进而才能进行更改元素位置的操作。
cpp 复制代码
class LFUCache {
public:
    LFUCache(int capacity) {
        capacity_ = capacity;        
        min_fre_ = 0;
    }
    int get(int key) {
        if(hash_.count(key) == 0)
            return -1;        
        int val = hash_[key]->val_;
        int fre = hash_[key]->fre_;

        list<Node>& l = lists_[fre];
        l.erase(hash_[key]);
        if(l.empty()) {
            lists_.erase(fre);
            if(min_fre_ == fre)
                min_fre_++;
        } 
        lists_[fre + 1].push_front(Node(key,val,fre + 1));
        hash_[key] = lists_[fre + 1].begin();
        return val;
    }
    
    void put(int key, int value) {
        if(hash_.count(key)) {
            hash_[key]->val_ = value;
            (void)get(key);
            return;
        }       
        
        if(capacity_ == 0) {
            list<Node>& l = lists_[min_fre_];
            int key = l.back().key_;
            hash_.erase(key);
            l.pop_back();
            if(l.empty())
                lists_.erase(min_fre_);
            capacity_++;
        }

        lists_[1].push_front(Node(key,value,1));
        min_fre_ = 1;
        hash_[key] = lists_[1].begin();
        capacity_--;
    }    
private:
    struct Node {
        int key_;
        int val_;
        int fre_;

        Node(int key,int val,int fre) {
            key_ = key;
            val_ = val;
            fre_ = fre;
        }
    };
    int capacity_;
    int min_fre_;
    unordered_map<int,list<Node>> lists_;
    unordered_map<int,list<Node>::iterator> hash_;
};
相关推荐
倒头就睡的小比特3 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
猎头南楼3 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
旖旎夜光3 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark3 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her13 天前
并查集-听课笔记
笔记·算法·并查集
码流子3 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
another heaven3 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
wzdark3 天前
从算法设计模式看编程思维的抽象能力4
算法
2601_962218613 天前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法