聊聊如何实现 LRU 缓存算法

聊聊缓存淘汰算法-LRU 实现原理 前言 我们常用缓存提升数据查询速度,由于缓存容量有限,当缓存容量到达上限,就需要删除部分数据挪出空间,这样新数据才可以添加进来。缓存数据不能随机删除,一般情况下我们需要根据某种算法删除缓存数据。常用淘汰算法有 LRU,LFU,FIFO,这篇文章我们聊聊 LRU 算法。 LRU 简介 LRU 是 Least Recently Used 的缩写,这种算法认为最近使用的数据是热门数据,下一次很大概率将会... 阅读详情

1. LRU 缓存介绍

LRU 算法全称是最近最少使用算法(Least Recently Use),是一种简单的缓存策略。顾名思义,LRU 算法会选出最近最少使用的数据进行淘汰。

那么什么是缓存呢?缓存专业点可以叫一种提高数据读取性能的技术,可以有效解决存储器性能和容量的矛盾,是一种空间换时间的设计思想,比如我们常见的内存是硬盘的缓存,Cache 是内存的缓存,浏览器本地存储是网络访问的缓存......

LRU 有许多应用场景,例如:

  • 操作系统底层的内存管理。
  • 缓存服务,例如 Redis,当数据满的时候就要淘汰掉长期不使用的 key,在 Redis 中用了一个类似的 LRU 算法,而不是严格的 LRU 算法。
  • MySQL 的 Buffer Pool,也就是缓冲池,它的目的是为了减少磁盘 IO。它是一块连续的内存,当 Buffer Pool 满的时候就要淘汰很久没有被访问过的页。

2. Leetcode 真题

146. LRU 缓存,请你设计并实现一个满足  LRU (最近最少使用) 缓存约束的数据结构。

实现 LRUCache 类:

  • LRUCache (int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存。
  • int get (int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
  • void put (int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该逐出最久未使用的关键字。

函数 get 和 put 必须以 O (1) 的平均时间复杂度运行。

示例:

输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]

解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1);    // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2);    // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1);    // 返回 -1 (未找到)
lRUCache.get(3);    // 返回 3
lRUCache.get(4);    // 返回 4

3. 题目分析

(1) 首先,题目中提到函数 get 和 put 必须以 O (1) 的平均时间复杂度运行,很自然地我们可以想到应该使用哈希表。

(2) 其次,当访问数据结构中的某个 key 时,需要将这个 key 更新为最近使用;另外如果 capacity 已满,需要删除访问时间最早的那条数据。这要求数据是有序的,并且可以支持在任意位置快速插入和删除元素,链表可以满足这个要求。

(3) 结合 1,2 两点来看,我们可以采用哈希表 + 链表的结构实现 LRU 缓存。

如上图所示,就是哈希表 + 链表实现的 LRU 缓存数据结构,有以下几个问题解释一下:

(1) 为什么这里要使用双向链表,而不是单向链表?我们在找到了节点,需要删除节点的时候,如果使用单向链表的话,后驱节点的指针是直接能拿到的,但是这里要求时间复杂度是 O (1),要能够直接获取到前驱节点的指针,那么只能使用双向链表。

(2) 哈希表里面已经保存了 key ,那么链表中为什么还要存储 key 和 value 呢,只存入 value 不就行了?当我们删除节点的时候,除了需要删除链表中的节点,还需要删除哈希表中的节点。删除哈希表中的节点需要知道 key,所以在链表的节点中需要存储 key 和 value,当删除链表节点时拿到 key,再根据 key 到哈希表中删除节点。

(3) 虚拟头节点和虚拟尾节点有什么用?虚拟节点在链表中被广泛应用,又称为哨兵节点,通常不保存任何数据。使用虚拟节点我们可以统一处理链表中所有节点的插入删除操作,而不用考虑头尾两个节点的特殊情况。

4. 代码实现

(1) Golang

package main

import "fmt"

// LRU 数据结构
type LRUCache struct {
  capacity   int                  // 容量
  size       int                  // 已使用空间
  head, tail *DLinkedNode         // 头节点,尾节点
  cache      map[int]*DLinkedNode // 哈希表
}

// 双向链表数据结构
type DLinkedNode struct {
  key, value int
  prev, next *DLinkedNode // 前指针,后指针
}

// 创建一个新的节点
func initDLinkedNode(key, value int) *DLinkedNode {
  return &DLinkedNode{
    key:   key,
    value: value,
  }
}

// 初始化 LRU 结构
func Constructor(capacity int) LRUCache {
  l := LRUCache{
    cache:    map[int]*DLinkedNode{}, //  哈希表
    head:     initDLinkedNode(0, 0),  // 虚拟头节点
    tail:     initDLinkedNode(0, 0),  // 虚拟尾节点
    capacity: capacity,               // 容量
  }
  // 虚拟头节点和虚拟尾节点互连
  l.head.next = l.tail
  l.tail.prev = l.head
  return l
}

// 获取元素
func (this *LRUCache) Get(key int) int {
  // 如果没有在哈希表中找到 key
  if _, ok := this.cache[key]; !ok {
    return -1
  }
  // 如果 key 存在,先通过哈希表定位,再移到头部
  node := this.cache[key]
  this.moveToHead(node)
  return node.value
}

// 插入元素
func (this *LRUCache) Put(key int, value int) {
  // 先去哈希表中查询
  // 如果 key 不存在,创建一个新的节点
  if node, ok := this.cache[key]; !ok {
    newNode := initDLinkedNode(key, value)
    // 如果达到容量限制,链表删除尾部节点,哈希表删除元素
    this.size++
    if this.size > this.capacity {
      // 得到删除的节点
      removed := this.removeTail()
      // 根据得到的 key 删除哈希表中的元素
      delete(this.cache, removed.key)
      // 减少已使用容量
      this.size--
    }
    // 插入哈希表
    this.cache[key] = newNode
    // 插入链表
    this.addToHead(newNode)
  } else { // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
    node.value = value
    this.moveToHead(node)
  }
}

// 将节点添加到头部
func (this *LRUCache) addToHead(node *DLinkedNode) {
  // 新节点指向前后节点
  node.prev = this.head
  node.next = this.head.next
  
  // 前后节点指向新节点
  this.head.next.prev = node
  this.head.next = node
}

// 删除该节点
func (this *LRUCache) removeNode(node *DLinkedNode) {
  // 修改该节点前后节点的指针,不再指向该节点
  node.next.prev = node.prev
  node.prev.next = node.next
}

// 移动到头部,也就是当前位置删除,再添加到头部
func (this *LRUCache) moveToHead(node *DLinkedNode) {
  this.removeNode(node)
  this.addToHead(node)
}

// 移除尾部节点,淘汰最久未使用的
func (this *LRUCache) removeTail() *DLinkedNode {
  node := this.tail.prev // 虚拟尾节点的上一个才是真正的尾节点
  this.removeNode(node)
  return node
}

// 打印链表(解题不需要此方法,只是为了显示效果)
func (this *LRUCache) printDLinkedNode() {
  p := this.head
  for p != nil {
    fmt.Printf("key: %d, value: %d\n", p.key, p.value)
    p = p.next
  }
}
func main() {
  lru := Constructor(3)
  fmt.Println("=========================== 插入 3 个节点 ===========================")
  lru.Put(1, 100)
  lru.Put(2, 200)
  lru.Put(3, 300)
  fmt.Println("=========================== 打印当前链表 ===========================")
  lru.printDLinkedNode()

  fmt.Println("=========================== 插入第 4 个节点,LRU 缓存淘汰尾部节点 ===========================")
  lru.Put(4, 400)
  lru.printDLinkedNode()

  fmt.Println("=========================== 获取 key2 节点,更新 LRU 缓存,将会移动至链表头部 ===========================")
  lru.Get(2)
  lru.printDLinkedNode()
}

(2) Java

import java.util.HashMap;  
import java.util.Map;  
  
public class LRUCache {  
    // 双向链表  
    class DLinkedNode {  
        int key;  
        int value;  
        DLinkedNode prev;  
        DLinkedNode next;  
  
        public DLinkedNode() {  
        }  
        public DLinkedNode(int key, int value) {  
            this.key = key;  
            this.value = value;  
        }  
    }  
    // 哈希表  
    private Map<Integer, DLinkedNode> cache = new HashMap<>();  
    // 已使用空间  
    private int size;  
    // 容量  
    private int capacity;  
    // 头节点,尾节点  
    private DLinkedNode head, tail;  
  
    public LRUCache(int capacity) {  
        this.size = 0;  
        this.capacity = capacity;  
        // 使用虚拟头部和虚拟尾部节点  
        head = new DLinkedNode();  
        tail = new DLinkedNode();  
        // 虚拟头节点和虚拟尾节点互连  
        head.next = tail;  
        tail.prev = head;  
    }  
  
    // 获取元素  
    public int get(int key) {  
        DLinkedNode node = cache.get(key);  
        // 如果没有在哈希表中找到 key  
        if (node == null) {  
            return -1;  
        }  
        // 如果 key 存在,先通过哈希表定位,再移到头部  
        moveToHead(node);  
        return node.value;  
    }  
  
    // 插入元素  
    public void put(int key, int value) {  
        DLinkedNode node = cache.get(key);  
        if (node == null) {  
            // 如果 key 不存在,创建一个新的节点  
            DLinkedNode newNode = new DLinkedNode(key, value);  
            // 如果达到容量限制,链表删除尾部节点,哈希表删除元素  
            size++;  
            if (size > capacity) {  
                // 得到删除的节点  
                DLinkedNode removed = removeTail();  
                // 根据得到的 key 删除哈希表中的元素  
                cache.remove(removed.key);  
                // 减少已使用容量  
                size--;  
            }  
            // 插入哈希表  
            cache.put(key, newNode);  
            // 添加至双链表的头部  
            addToHead(newNode);  
        } else {  
            // 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部  
            node.value = value;  
            moveToHead(node);  
        }  
    }  
    // 将节点添加到链表头部  
    private void addToHead(DLinkedNode node) {  
        // 新节点指向前后节点  
        node.prev = head;  
        node.next = head.next;  
        // 前后节点指向新节点  
        head.next.prev = node;  
        head.next = node;  
    }  
  
    // 删除节点  
    private void removeNode(DLinkedNode node) {  
        // 修改该节点前后节点的指针,不再指向该节点  
        node.prev.next = node.next;  
        node.next.prev = node.prev;  
    }  
  
    // 移动到头部,也就是当前位置删除,再添加到头部  
    private void moveToHead(DLinkedNode node) {  
        removeNode(node);  
        addToHead(node);  
    }  
  
    // 移除尾部节点,淘汰最久未使用的  
    private DLinkedNode removeTail() {  
        DLinkedNode res = tail.prev; // 虚拟尾节点,prev 才是此时真正的尾节点  
        removeNode(res);  
        return res;  
    }  
  
    // 打印链表(解题不需要此方法,只是为了显示效果)  
    private void printDLinkedNode() {  
        DLinkedNode p = this.head;  
        while (p != null) {  
            System.out.printf("key: %d, value: %d\n", p.key, p.value);  
            p = p.next;  
        }  
    }  
    public static void main(String[] args) {  
        LRUCache lru = new LRUCache(3);  
        System.out.println("=========================== 插入 3 个节点 ===========================");  
        lru.put(1, 100);  
        lru.put(2, 200);  
        lru.put(3, 300);  
        System.out.println("=========================== 打印当前链表 ===========================");  
        lru.printDLinkedNode();  
  
        System.out.println("=========================== 插入第 4 个节点,LRU 缓存淘汰尾部节点 key1 ===========================");  
        lru.put(4, 400);  
        lru.printDLinkedNode();  
  
        System.out.println("=========================== 获取 key2 的节点,更新 LRU 缓存,将会移动至链表头部 ===========================");  
        lru.get(2);  
        lru.printDLinkedNode();  
    }  
}

(3) 运行结果

代码运行的返回结果如下,其中头尾两个 key=0, value=0 的节点是虚拟节点,请忽略。

=========================== 插入 3 个节点 ===========================
=========================== 打印当前链表 ===========================
key: 0, value: 0
key: 3, value: 300
key: 2, value: 200
key: 1, value: 100
key: 0, value: 0
=========================== 插入第 4 个节点,LRU 缓存淘汰尾部节点 ===========================
key: 0, value: 0
key: 4, value: 400
key: 3, value: 300
key: 2, value: 200
key: 0, value: 0
=========================== 获取 key2 节点,更新 LRU 缓存,将会移动至链表头部 ===========================
key: 0, value: 0
key: 2, value: 200
key: 4, value: 400
key: 3, value: 300
key: 0, value: 0

5. 测试案例示意图

第 1 步:初始化数据结构。

第 2 步:插入节点 key1。

第 3 步:插入节点 key2。 此时 key2 插入到链表头部。

第 4 步:插入节点 key3。 此时 key3 插入到链表头部。

第 5 步:插入节点 key4。当前 capacity 容量达到上限(3),分为 2 步:

使用 removeTail()​ 方法删除链表尾部的节点 key1,从 removeTail() 方法的返回值得到 node,再根据 node.key 得到 key1,然后去哈希表删除节点 key1。

然后插入节点 key4,此时 key4 在链表头部。

第 6 步:读取 key2 的值,将 key2 移动到链表头部。

聊聊Redis中缓存淘汰算法的实现 的核心实现,可以看到其内部会检查当前使用的内存并查看是否超过最大内存上限,如果超过了则根据策略定位数据,则进入循环每次淘汰一批元素直到内存空间充裕为止,以笔者为例配置的是。通过上文的解析我们了解redis缓存淘汰算法的基本设计思路,我们不妨通过源码的方式加以印证,当调用set等指令添加新的键值对的时候,redis就会给这个键值对的。因为近期收到很多读者的私信,所以也专门创建了一个交流群,感兴趣的读者可以通过上方的公众号获取笔者的联系方式完成好友添加,点击备注。 阅读详情

相关推荐

面试官:你知道 LRU算法 —— 缓存淘汰算法吗?

常用缓存提升数据查询速度,由于缓存容量有限,当缓存容量到达上限,就需要删除部分数据挪出空间,这样新数据才可以添加进来。缓存数据不能随机删除,一般情况下我们需要根据某种算法删除缓存数据。常用淘汰算法有 LRU,LFU,FIFO。这篇文章我们聊聊 LRU 算法。 一、LRU 简介 LRU 是 Least Recently Used 的缩写,这种算法认为最近使用的数据是热门数据,下一次很大概率将会再次被使用。而最近很少被使用的数据,很大概率下一次不再用到。当缓存容量的满时候,优先淘汰最近很少使用的数据。 假设

1万+

LRU的实现

一、什么是缓存 这里说的缓存是一种广义的概念,在计算机存储层次结构中,低一层的存储器都可以看做是高一层的缓存。比如Cache是内存的缓存,内存是硬盘的缓存,硬盘是网络的缓存等等。 缓存可以有效地解决存储器性能与容量的这对矛盾,但绝非看上去那么简单。如果缓存算法设计不当,非但不能提高访问速度,反而会使系统变得更慢。 从本质上来说,缓存之所以有效是因为程序和数据的局部性(locality)。程序...

轻描时光 1730

冷热算法:缓存淘汰算法LRU以及冷热数据分析应用

常用缓存提升数据查询速度,由于缓存容量有限,当缓存容量到达上限,就需要删除部分数据挪出空间,这样新数据才可以添加进来。缓存数据不能随机删除,一般情况下我们需要根据某种算法删除缓存数据。常用淘汰算法有 LRU,LFU,FIFO。这篇文章我们聊聊 LRU 算法。LRU 是( Least Recently Used) 的缩写,这种算法认为最近使用的数据是热门数据,下一次很大概率将会再次被使用。而最近很少被使用的数据,很大概率下一次不再用到。当缓存容量的满时候,优先淘汰最近很少使用的数据。其在Redis、Guava

GIS摆渡人 705

LRU算法四种实现方式介绍

LRU全称是Least Recently Used,即最近最久未使用的意思。 LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。 实现LRU       1.用一个数组来存储数据,给每一个数据项标记一个访问时间戳,每次插入新数据项的时候,先把数组中存

elricboa的专栏 20万+

Arduino程序设计(十)8×8 共阴极LED点阵显示(MAX7219)

本文主要介绍8×8 LED点阵显示实验,分别是:1、介绍MAX7219点阵模块;2、点阵显示部分图形;3、点阵显示数字 0~9 及 26 个英文字母。1、通过上述两个点阵实验,了解了8*8点阵显示的基本原理;2、通过SPI通信控制点阵显示,学会了Arduino用SPI通信方式控制外设。

Alice的博客 1万+

Android 16(API 36)状态栏颜色适配全攻略:从沉浸式到自定义背景

本文全面解析Android 16(API 36)强制沉浸式状态栏带来的适配挑战,提供了从FrameLayout包裹法到动态修改Window背景色的核心解决方案。针对传统状态栏颜色设置失效问题,详细阐述了如何利用WindowInsets API实现自定义背景,并涵盖全屏模式、Fragment切换及第三方ROM兼容性等实战场景,为开发者提供一站式适配指南。

weixin_29227425的博客 597

一篇搞懂什么是LRU缓存|一篇搞懂LRU缓存的实现|LRUCache详解和实现

一篇搞懂LRU缓存的原理

系统编程、网络编程 1万+

Vue中实现word文档渲染、在线编辑、保存

Vue3对word文档进行渲染、编辑、保存。word转成html,html转成word

h18749849699的博客 1331

LRU缓存机制

计算机的缓存容量有限,如果缓存满了就要删除一些内容,给新内容腾位置。那么,什么样的数据,我们判定为「有用的」的数据呢?LRU 的全称是 Least Recently Used,也就是说我们认为最近使用过的数据应该是「有用的」,很久都没用过的数据应该是无用的,内存满了就优先删那些很久没用过的数据。常见的场景如手机的后台运行缓存,我们依次打开了[王者荣耀] [淘宝] [微信] [抖音],假设手机只允许同时打开个应用程序,那么在我们打开抖音的那一刻,就会将最久未使用的 [王者荣耀] 关闭,腾出空间给 抖音。

技术分享博客 1413

自学PCB思路 | PCB设计零基础入门-小白适用(附详细入门经验分享)

本人大一在实践课上就听说PCB,被虎的一楞一楞的,不过现在回看,发现其实并没有相信的那么难!想着把自己这一路摸索的学习路线分享一下,当时我是一边摸索一边上手,一把鼻涕一把泪的过来的。如果你想搞硬件或者需要搞硬件,下面是我的入门学习过程,希望可以帮到你。目录前言选择一款设计软件好的入门课程掌握抄板元件采购焊板子烙铁头热风枪自己半设计明确设计需求全设计最后我并非什么大佬,也没法自己去教大家如何画板。

自动化小秋葵的博客 1万+

《民机适航入门指南:从法规学习到项目实践的全面解析》

适航(Airworthiness)是指航空器、其发动机、螺旋桨和其他部件在规定条件下,能够安全飞行并满足有关航空法规规定的条件。简而言之,适航代表了航空器在任何时候都能安全运营的状态。适航在民用航空领域尤为关键,因为飞机必须经过严格的适航认证才能投入商用。这些认证的目标是确保飞机能够在广泛的运行条件下保持飞行安全,从而保护乘客、机组和地面人员的生命财产安全。适航法规覆盖了飞机的整个生命周期,包括设计、制造、测试、运营、维护和报废。

qq_41854911的博客 995

【常见缓存算法原理及其C++实现】LRU篇(包含过期时间详解)

一、缓存算法简介 (一)缓存算法应用背景 缓存的应用场景和范围十分广泛,下面给出其十分常见的两种应用背景: 首先,在操作系统内部,由于内存资源十分有限,而每个进程又都希望独享一块很大的内存空间。所以诞生了一种“虚拟内存”机制,它将进程的一部分内容暂留在磁盘中,在需要时再进行数据交换将其放入内存,这个过程就需要用到缓存算法机制进行置换。 其次,对于各类应用项目开发而言,在巨大的数据量面前,Catch 是不可或缺的。因为无论是针对本地端的浏览器缓存,还...

saxon_li的博客 1万+

循迹小车控制实验:代码集成与硬件验证

在main.c文件中还需定义上层代码调用的GPIO输入和PWM输出相关函数,其中调用STM32CubeMX生成的HAL库函数。}else{}else{}else{}else{在main.h文件中的USER CODE BEGIN EFP注释代码段中外部声明Get函数和Set函数,使该函数可以被上层代码调用。循迹小车控制实验到本章全部完成。相比于前几章节的实验,本实验复杂程度是最高的。

u013288925的博客 127

mysql lru_聊聊缓存淘汰算法-LRU 实现原理

前言我们常用缓存提升数据查询速度,由于缓存容量有限,当缓存容量到达上限,就需要删除部分数据挪出空间,这样新数据才可以添加进来。缓存数据不能随机删除,一般情况下我们需要根据某种算法删除缓存数据。常用淘汰算法有 LRU,LFU,FIFO,这篇文章我们聊聊 LRU 算法。LRU 简介LRU 是 Least Recently Used 的缩写,这种算法认为最近使用的数据是热门数据,下一次很大概率将会再次被...

weixin_39992831的博客 546

YOLOv11改进 | 模型知识蒸馏篇 | 利用模型蒸馏改进YOLOv11进行无损涨点 | CWDLoss(在线蒸馏 + 离线蒸馏)

这篇文章给大家带来最新改进的是模型的蒸馏利用教师模型指导学生模型从而进行模型的涨点,本文的内容不仅可以用于论文中,在目前的绝大多数的工作中模型蒸馏是一项非常重要的技术,所以大家可以仔细学习一下本文的内容,本文从YOLOv11的项目文件为例,进行详细的修改教程,文章内包括完整的修改教程,针对小白我出了视频修改教程,如果你还不会我提供了修改后的文件大家直接运行即可,所以说不用担心不会适用!模型蒸馏真正的无损涨点,蒸馏你只看这一篇文章就足够了!欢迎大家订阅我的专栏一起学习YOLO!

Snu77的博客 3331

我正用AI Agent重构传统GIS 核心功能,说大白话做空间分析

GeoAI-UP项目通过AI Agent技术实现自然语言驱动的空间分析,核心创新包括: 意图识别层:通过LLM理解用户需求,自动拆解为可执行目标 智能任务规划:动态生成分析流程,处理复杂依赖关系 统一算子架构:采用标准化接口封装200+空间分析功能 可视化工作流。

专注GIS软件开发。 496

java模拟缓存淘汰算法_聊聊缓存淘汰算法-LRU 实现原理

前言我们常用缓存提升数据查询速度,由于缓存容量有限,当缓存容量到达上限,就需要删除部分数据挪出空间,这样新数据才可以添加进来。缓存数据不能随机删除,一般情况下我们需要根据某种算法删除缓存数据。常用淘汰算法有 LRU,LFU,FIFO,这篇文章我们聊聊 LRU 算法。LRU 简介LRU 是 Least Recently Used 的缩写,这种算法认为最近使用的数据是热门数据,下一次很大概率将会再次被...

weixin_39695672的博客 137

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

mysql缓存淘汰机制_聊聊缓存淘汰算法-LRU 实现原理

前言我们常用缓存提升数据查询速度,由于缓存容量有限,当缓存容量到达上限,就需要删除部分数据挪出空间,这样新数据才可以添加进来。缓存数据不能随机删除,一般情况下我们需要根据某种算法删除缓存数据。常用淘汰算法有 LRU,LFU,FIFO,这篇文章我们聊聊 LRU 算法。LRU 简介LRU 是 Least Recently Used 的缩写,这种算法认为最近使用的数据是热门数据,下一次很大概率将会再次被...

weixin_28834765的博客 1048

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

java先进先出淘汰算法_聊聊缓存淘汰算法-LRU 实现原理

前言我们常用缓存提升数据查询速度,由于缓存容量有限,当缓存容量到达上限,就需要删除部分数据挪出空间,这样新数据才可以添加进来。缓存数据不能随机删除,一般情况下我们需要根据某种算法删除缓存数据。常用淘汰算法有 lru,lfu,fifo,这篇文章我们聊聊 lru 算法。lru 简介lru 是 least recently used 的缩写,这种算法认为最近使用的数据是热门数据,下一次很大概率将会再次被...

weixin_34851528的博客 194

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

缓存淘汰算法-LRU 实现原理

01、前言 我们常用缓存提升数据查询速度,由于缓存容量有限,当缓存容量到达上限,就需要删除部分数据挪出空间,这样新数据才可以添加进来。缓存数据不能随机删除,一般情况下我们需要根据某种算法删除缓存数据。常用淘汰算法有 LRU,LFU,FIFO,这篇文章我们聊聊 LRU 算法。 02、LRU 简介 LRU是 Least Recently Used 的缩写,这种算法认为最近使用的数据是热门数据,下一次很大概率将会再次被使用。而最近很少被使用的数据,很大概率下一次不再用到。当缓存容量的满时候,优先淘汰最近很.

luzhensmart的专栏 918

缓存淘汰算法-LRU

我们常用缓存来提升数据查询速度,由于缓存容量有限,当缓存容量到达上限,就需要删除部分数据挪出空间,这样新数据才可以添加进来。缓存数据不能随机删除,一般情况下我们需要根据某种算法删除缓存数据。常用淘汰算法有 LRU,LFU,FIFO,这篇文章我们聊聊 LRU 算法。例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

yangyanping20108的博客 535

LRU 实现原理

前言 我们常用缓存提升数据查询速度,由于缓存容量有限,当缓存容量到达上限,就需要删除部分数据挪出空间,这样新数据才可以添加进来。缓存数据不能随机删除,一般情况下我们需要根据某种算法删除缓存数据。常用淘汰算法有 LRU,LFU,FIFO,这篇文章我们聊聊 LRU 算法。 LRU 简介 LRU 是 Least Recently Used 的缩写,这种算法认为最近使用的数据是热门数据,下一次很大概率将会再次被使用。而最近很少被使用的数据,很大概率下一次不再用到。当缓存容量的满时候,优先淘汰最近很少使用的数据。 假

稻草人技术博客 2684

算法:leetcode_146_LRU 缓存

本文详细介绍了LRU(最近最少使用)缓存的设计与实现。通过双向循环链表和哈希表的结合,实现了O(1)时间复杂度的get和put操作。核心实现包括:1)使用双向链表维护访问顺序;2)利用哈希表实现快速查找;3)定义disconnect和pushFront方法维护链表结构;4)处理容量满时的淘汰策略。

bugMaker_0621的博客 1245

【Redis笔记】一起学习Redis | 聊聊Redis的内存淘汰LRU算法?

一起学习Redis | 聊聊Redis的LRU内存淘汰算法? 前提概要 常见问题 Redis的过期策略有哪些? 聊一聊Redis的内存淘汰算法? 手写一个LRU代码实现? 我插入到Redis中的数据怎么没啦? 我明明给Redis的数据设置了过期时间,时间到了,为什么那些数据还占用着内存? 为什么需要内存淘汰算法? 讲一个小场景 一名开发人员经常吐槽Redis有Bug,说他们的生产环境中的Re...

长路漫漫的歇脚处 1519

LRU-K算法真的比LRU强吗?结合Redis与MySQL实战聊聊缓存替换策略的选择

本文深入探讨了LRU-K算法与传统LRU在缓存替换策略中的优劣对比,结合Redis与MySQL的实战案例进行分析。LRU-K通过记录K次访问历史,有效解决了LRU在突发扫描查询等场景下的缓存污染问题,但需权衡内存开销与计算复杂度。文章还提供了配置建议和现代系统中的替代方案,帮助开发者根据实际需求选择合适的缓存策略。

weixin_30652491的博客 142

淘汰算法LRU是什么?

01、前言 我们常用缓存提升数据查询速度,由于缓存容量有限,当缓存容量到达上限,就需要删除部分数据挪出空间,这样新数据才可以添加进来。缓存数据不能随机删除,一般情况下我们需要根据某种算法删除缓存数据。常用淘汰算法有 LRU,LFU,FIFO,这篇文章我们聊聊 LRU 算法。 02、LRU 简介 LRU是 Least Recently Used 的缩写,这种算法认为最近...

乔治大哥的博客 888
上一篇: 微服务之服务注册和服务发现篇
下一篇: 40 个 SpringBoot 常用注解:让生产力爆表
JavaMonsterr
博客等级 码龄4年 211粉丝 481原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值