数据结构与算法面试题:实现一个 LRU 缓存,支持如下操作:获取值、更新值、删除键值对和插入键值对

简介: 数据结构与算法面试题:实现一个 LRU 缓存,支持如下操作:获取值、更新值、删除键值对和插入键值对

数据结构与算法面试题:实现一个 LRU 缓存,支持如下操作:获取值、更新值、删除键值对和插入键值对

简介:实现一个 LRU 缓存,支持如下操作:获取值、更新值、删除键值对和插入键值对

算法思路

  1. 使用一个双向链表存储每个键值对,按照访问时间从早到晚依次排列,越晚访问的节点越靠近双向链表的头部。这里使用了 C++ 中的 list 模板类。
  2. 使用一个哈希表存储键和对应的节点指针,可以用 C++ 标准库中的 unordered_map 实现。
  3. 对于插入、更新、删除操作需要同时修改双向链表和哈希表。
  4. 当缓存已满时,在插入新的键值对之前,需要将最近最少使用的节点从双向链表中删除,并从哈希表中删除相应的键值对。
  • c++
#include <iostream>
#include <unordered_map>
#include <list>
using namespace std;
class LRUCache {
public:
    LRUCache(int capacity) { // 构造函数
        cap = capacity; // 缓存容量
    }
    int get(int key) { // 获取值操作
        if (cache.count(key)) { // 缓存命中
            auto it = cache[key]; // 从哈希表中找到对应的迭代器
            int val = it->second; // 取出键值对中的值
            recent.erase(it); // 将访问过的键位于链表头部,将其删除
            recent.push_front(key); // 将当前键放在链表头部
            cache[key] = recent.begin(); // 更新键在双向链表中的对应迭代器位置
            return val;
        }
        return -1; // 缓存未命中
    }
    void put(int key, int value) { // 更新值操作
        if (cache.count(key)) { // 若键已存在,则替换其值
            auto it = cache[key]; // 从哈希表中找到对应的迭代器
            recent.erase(it); // 将访问过的键位于链表头部,将其删除
        }
        else if (recent.size() == cap) { // 若链表已满,则删除最久未使用的键
            int old_key = recent.back(); // 查找链表尾部的键值并保留
            recent.pop_back(); // 删除链表尾部的键值对
            cache.erase(old_key); // 从哈希表中删除对应的键
        }
        recent.push_front(key); // 将当前键放在链表头部
        cache[key] = recent.begin(); // 更新键在双向链表中的对应迭代器位置
        cache[key]->second = value; // 更新键值对中的值
    }
private:
    int cap;
    unordered_map<int, list<int>::iterator> cache; // 哈希表,键为键值,值为双向链表中对应节点的迭代器
    list<int> recent; // 双向链表,存储键值
};
int main() {
    LRUCache lru_cache(2);
    lru_cache.put(1, 1);
    lru_cache.put(2, 2);
    cout << lru_cache.get(1) << endl; // 查询键1,返回1,并将该键移动到链表头部
    lru_cache.put(3, 3); // 插入键3,此时容量超出限制,删除最近最少使用的键2
    cout << lru_cache.get(2) << endl; // 查询键2,未命中,返回-1
    lru_cache.put(4, 4); // 插入键4,此时容量超出限制,删除最近最少使用的键1
    cout << lru_cache.get(1) << endl; // 查询键1,未命中,返回-1
    cout << lru_cache.get(3) << endl; // 查询键3,返回3,并将该键移动到链表头部
    cout << lru_cache.get(4) << endl; // 查询键4,返回4,并将该键移动到链表头部
    return 0;
}
  • java
import java.util.*;
class LRUCache {
    private int cap;
    private Map<Integer, Integer> cache; // 哈希表,键为键值,值为对应的值
    private Deque<Integer> recent; // 双向链表,存储键值
    public LRUCache(int capacity) { // 构造函数
        cap = capacity; // 缓存容量
        cache = new HashMap<>();
        recent = new LinkedList<>();
    }
    public int get(int key) { // 获取值操作
        if (cache.containsKey(key)) { // 缓存命中
            recent.remove(key); // 移除双向链表中的键
            recent.addFirst(key); // 将当前键放在链表头部
            return cache.get(key); // 返回值
        }
        return -1; // 缓存未命中
    }
    public void put(int key, int value) { // 更新值操作
        if (cache.containsKey(key)) { // 若键已存在,则替换其值
            recent.remove(key); // 移除双向链表中的键
        }
        else if (recent.size() == cap) { // 若链表已满,则删除最久未使用的键
            int old_key = recent.removeLast(); // 查找链表尾部的键值并保留
            cache.remove(old_key); // 从哈希表中删除对应的键值对
        }
        recent.addFirst(key); // 将当前键放在链表头部
        cache.put(key, value); // 更新键值对中的值
    }
}
public class Main {
    public static void main(String[] args) {
        LRUCache lru_cache = new LRUCache(2);
        lru_cache.put(1, 1);
        lru_cache.put(2, 2);
        System.out.println(lru_cache.get(1)); // 查询键1,返回1,并将该键移动到链表头部
        lru_cache.put(3, 3); // 插入键3,此时容量超出限制,删除最近最少使用的键2
        System.out.println(lru_cache.get(2)); // 查询键2,未命中,返回-1
        lru_cache.put(4, 4); // 插入键4,此时容量超出限制,删除最近最少使用的键1
        System.out.println(lru_cache.get(1)); // 查询键1,未命中,返回-1
        System.out.println(lru_cache.get(3)); // 查询键3,返回3,并将该键移动到链表头部
        System.out.println(lru_cache.get(4)); // 查询键4,返回4,并将该键移动到链表头部
    }
}


相关文章
|
11月前
|
人工智能 算法 NoSQL
LRU算法的Java实现
LRU(Least Recently Used)算法用于淘汰最近最少使用的数据,常应用于内存管理策略中。在Redis中,通过`maxmemory-policy`配置实现不同淘汰策略,如`allkeys-lru`和`volatile-lru`等,采用采样方式近似LRU以优化性能。Java中可通过`LinkedHashMap`轻松实现LRUCache,利用其`accessOrder`特性和`removeEldestEntry`方法完成缓存淘汰逻辑,代码简洁高效。
513 0
|
存储 缓存 NoSQL
【Azure Redis 缓存】关于Azure Cache for Redis 服务在传输和存储键值对(Key/Value)的加密问题
【Azure Redis 缓存】关于Azure Cache for Redis 服务在传输和存储键值对(Key/Value)的加密问题
310 2
|
10月前
|
缓存 人工智能 算法
lru算法设计与实现
本文详细介绍了LRU(Least Recently Used,最近最少使用)缓存淘汰策略的原理与实现。LRU的核心思想是:越近被访问的数据,未来被再次访问的可能性越大。文章通过Java语言实现了一个支持O(1)时间复杂度操作的LRU缓存
424 0
|
算法
面试场景题:如何设计一个抢红包随机算法
本文详细解析了抢红包随机算法的设计与实现,涵盖三种解法:随机分配法、二倍均值法和线段切割法。随机分配法通过逐次随机分配金额确保总额不变,但易导致两极分化;二倍均值法优化了金额分布,使每次抢到的金额更均衡;线段切割法则将总金额视为线段,通过随机切割点生成子金额,手气最佳金额可能更高。代码示例清晰,结果对比直观,为面试中类似算法题提供了全面思路。
1948 16
|
算法 安全 Java
Java线程调度揭秘:从算法到策略,让你面试稳赢!
在社招面试中,关于线程调度和同步的相关问题常常让人感到棘手。今天,我们将深入解析Java中的线程调度算法、调度策略,探讨线程调度器、时间分片的工作原理,并带你了解常见的线程同步方法。让我们一起破解这些面试难题,提升你的Java并发编程技能!
605 16
|
算法 Java 数据库
美团面试:百亿级分片,如何设计基因算法?
40岁老架构师尼恩分享分库分表的基因算法设计,涵盖分片键选择、水平拆分策略及基因法优化查询效率等内容,助力面试者应对大厂技术面试,提高架构设计能力。
美团面试:百亿级分片,如何设计基因算法?
|
算法 前端开发 Java
数据结构与算法学习四:单链表面试题,新浪、腾讯【有难度】、百度面试题
这篇文章总结了单链表的常见面试题,并提供了详细的问题分析、思路分析以及Java代码实现,包括求单链表中有效节点的个数、查找单链表中的倒数第k个节点、单链表的反转以及从尾到头打印单链表等题目。
216 1
数据结构与算法学习四:单链表面试题,新浪、腾讯【有难度】、百度面试题
|
缓存 NoSQL PHP
Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出
本文深入探讨了Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出。文章还介绍了Redis在页面缓存、数据缓存和会话缓存等应用场景中的使用,并强调了缓存数据一致性、过期时间设置、容量控制和安全问题的重要性。
320 5
|
存储 缓存 算法
分布式缓存有哪些常用的数据分片算法?
【10月更文挑战第25天】在实际应用中,需要根据具体的业务需求、数据特征以及系统的可扩展性要求等因素综合考虑,选择合适的数据分片算法,以实现分布式缓存的高效运行和数据的合理分布。
|
机器学习/深度学习 算法 Java
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
下一篇
开通oss服务