手把手教学----------带你手撕LFU

本文详细介绍了LFU(Least Frequently Used)缓存淘汰策略的原理,并提供了一段C++实现代码。LFU策略优先淘汰最久未使用且使用频率最低的元素。文章通过`put`和`get`操作解释了如何维护频率块和键值映射,并讨论了频率块清空及最小频率更新的处理。核心数据结构包括`unordered_map`来存储频率块和键值迭代器,确保高效查找和更新。

手把手带你手撕LFU


LFU的淘汰策略是优先淘汰最久的使用频率最低的那个

所以

第一步

put

  • 如果没有超过容量,需要知道key对应的插入那么一个频率块,需要知道当前的频率块在里面删除
  • 如果超过了,要知道最小的频率在哪里

get

  • 快速知道key对应的value在哪里
  • 需要快速的删除,插入

所以我们设计如下

struct Node: 包括key value freq 三个属性

unordered_map<int,List<Node>> // key是频率 value是对应存放频率的地方

unordered_map<int,List<NOde>::iter> // key 是键值 value 指向key存放的位置

代码

#include <bits/stdc++.h>
using namespace std;

struct Node {
  int key;
  int value;
  int freq;
  Node(int a, int b, int c) : key(a), value(b), freq(c) {}
};

class LFUCache {
 public:
  LFUCache(int capacity) : size_(capacity) {}

  int get(int key) {
    int res = -1;

    if (key2iter_.find(key) != key2iter_.end()) {  // 如果找到了
      auto iter = key2iter_[key];
      Node node = *iter;
      res = node.value;
      delete_key(iter->key);
      node.freq++;
      addkey(node.key, node.value, node.freq);
    }
    return res;
  }

  void delete_key(int key) {  // 删除某个键为key node
    auto iter = key2iter_[key];
    int delete_freq = iter->freq;
    preq2data_[delete_freq].erase(iter);
    key2iter_.erase(key);
    if (preq2data_[delete_freq].empty()) {  // 删除完以后空了
      preq2data_.erase(delete_freq);        // 链表相应去除
      if (delete_freq == Min_freq) {
        Min_freq++;
      }
    }
  }

  void addkey(int key, int value, int freq) { // 在某个频率块加一个node
    // 新增的fre是1
    preq2data_[freq].push_back(Node(key, value, freq));
    key2iter_[key] = --preq2data_[freq].end();
    if (freq == 1) {
      Min_freq = 1;
    }
  }

  void put(int key, int value) {
    if (size_ == 0) {
      return;
    }
    if (key2iter_.find(key) == key2iter_.end()) {  // 原来没有这个节点
      if (key2iter_.size() == size_) {             // 容量达到最大
        // 先进行淘汰
        delete_key(preq2data_[Min_freq].front().key);
        addkey(key, value, 1);
      } else {
        addkey(key, value, 1);
      }
    } else {  // 存在
      Node node = *key2iter_[key];
      delete_key(key);
      node.freq++;
      node.value = value;
      addkey(key, value, node.freq);
    }
    //  show();
  }

 private:
  unordered_map<int, list<Node>> preq2data_;
  unordered_map<int, list<Node>::iterator> key2iter_;
  int size_;
  int Min_freq = 0;
};

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

这里的关键点就是

  • pre2data的键是频率一定不可以弄错
  • 如果这个pre2data某一个值被我们删除完了,那么需要把对用的频率进行删除,如果删除的频率等于最小的频率那么最小频率直接加1
  • 为什么最小频率直接加1,因为删除之后出现在get以及put之后,put会把min_freq置1,而加一是因为,get后频率会加一
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值