0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

设计并实现一个满足LRU约束的数据结构

冬至子 来源:i余数 作者:i余数 2023-06-07 17:05 次阅读

题目

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

实现 LRUCache 类:

  • LRUCache(int capacity)「正整数」 作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1
  • void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,

则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 「逐出」 最久未使用的关键字。

1.jpg

示例:

输入
["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

提示:

1.jpg

题解(哈希表 + 双向链表)

解题之前先了解一下什么是 「LRU缓存」LRU 的英文全称为 Latest Recently Used,即 「最近最少使用」 。在缓存占满的时候,先删除最旧的数据。

1.jpg

Java 代码实现

class LRUCache {

    private int capacity;

    private Map< Integer, ListNode > cache;

    // 保护节点,protectHead.next 为head节点, protectTail.pre为tail节点
    private ListNode protectHead = new ListNode();
    private ListNode protectTail = new ListNode();

    public LRUCache(int capacity) {
        this.capacity = capacity;
        cache = new HashMap<  >(capacity);
        protectHead.next = protectTail;
        protectTail.pre = protectHead;
    }


    // 删除指定节点
    private void remove(ListNode listNode){
        listNode.pre.next = listNode.next;
        listNode.next.pre = listNode.pre;

        listNode.pre = null;
        listNode.next = null;
    }

    // 添加到末尾
    private void addToTail(ListNode listNode){

        protectTail.pre.next = listNode;
        listNode.pre = protectTail.pre;

        listNode.next = protectTail;
        protectTail.pre = listNode;
        
    }

    // 从当前位置移动到末尾
    private void moveToTail(ListNode listNode){
        
        this.remove(listNode);
        this.addToTail(listNode);
        
    }
    
    public int get(int key) {
        if(cache.containsKey(key)){
            ListNode listNode = cache.get(key);
            this.moveToTail(listNode);
            return listNode.value;
        }else{
            return -1;
        }

    }
    
    public void put(int key, int value) {
        if(cache.containsKey(key)){
            // 将 key 移动到最新的位置
            // 1. 在旧的位置删除
            // 2. 追加key到链表末尾
            ListNode listNode = cache.get(key);

            // 这里必须重新赋值,虽然缓冲已经存在了,但是可能值不一样。
            listNode.value = value;
            this.moveToTail(listNode);
            return;

        }

        if(cache.size() == capacity){
            // 1. 找到最旧的数据,也就是链表的head,删除head
            // 2. 在cache map 中删除 head对应的key
            ListNode headNode = protectHead.next;
            this.remove(headNode);
            cache.remove(headNode.key);
        }


        // 1. 添加新的key到cache map
        // 2. 追加新的key到链表末尾

        ListNode listNode = new ListNode();
        listNode.key = key;
        listNode.value = value;

        this.addToTail(listNode);
        cache.put(key, listNode);

    }
}

class ListNode{
    int key;
    int value;
    ListNode pre;
    ListNode next;

}

Go 代码实现

// 定义双向链表
type MyListNode struct {
    key, value int
    pre, next *MyListNode
}

type LRUCache struct { 
    size, capacity int
    cache map[int]*MyListNode
    protectHead, protectTail *MyListNode
}


func Constructor(capacity int) LRUCache {
    lruCache := LRUCache{
        size: 0,
        capacity: capacity,
        cache: map[int]*MyListNode{},
        protectHead: &MyListNode{},
        protectTail: &MyListNode{},
    }

    lruCache.protectHead.next = lruCache.protectTail
    lruCache.protectTail.pre = lruCache.protectHead
    return lruCache
}


func (this *LRUCache) Get(key int) int {
    if listNode, ok := this.cache[key]; ok {
        this.moveToTail(listNode);
        return listNode.value;
    }else{
        return -1
    }
}


func (this *LRUCache) Put(key int, value int)  {
    if listNode, ok := this.cache[key]; ok {
        listNode.value = value;
        this.moveToTail(listNode);
        return;
    }
    if(this.size == this.capacity){
        headNode := this.protectHead.next;
        this.remove(headNode);
        delete(this.cache, headNode.key);
        this.size--
    }

    listNode := &MyListNode{
        key: key,
        value: value,
    }


    this.addToTail(listNode)
    this.cache[key] = listNode
    this.size++
}

// 删除指定节点
func (this *LRUCache) remove(listNode *MyListNode){
    listNode.pre.next = listNode.next;
    listNode.next.pre = listNode.pre;

    listNode.pre = nil;
    listNode.next = nil;
}


// 添加到末尾
func (this *LRUCache) addToTail(listNode *MyListNode){
    this.protectTail.pre.next = listNode
    listNode.pre = this.protectTail.pre

    listNode.next = this.protectTail
    this.protectTail.pre = listNode
}


// 从当前位置移动到末尾
func (this *LRUCache) moveToTail(listNode *MyListNode){
    this.remove(listNode);
    this.addToTail(listNode);
}

复杂度分析

1.jpg

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • JAVA
    +关注

    关注

    19

    文章

    2978

    浏览量

    105288
  • cache技术
    +关注

    关注

    0

    文章

    41

    浏览量

    1099
收藏 人收藏

    评论

    相关推荐

    什么是数据结构(Data Structrue)

    一个一个元素数据对象:具有相同特性的数据元素的集合结构数据元素之间具有的关系(联系) 二. 
    发表于 02-09 17:17

    GPIB命令的数据结构

    GPIB命令结点;考虑程序实现的效率问题以及管理维护方面的因素,对普通的树进行改造,从而形成特有的"GPIB命令树"。【关键词】:通用接口总线(GPIB);;数据结构;;树
    发表于 04-24 09:44

    【原创】Android开发—Lru核心数据结构实现突破缓存框架

    【原创】Android开发—Lru核心数据结构实现突破缓存框架回复即可获取下载链接[hide=d15]链接:http://pan.baidu.com/s/1c2BfjsW 密码:bta5 更多学习资料加Q:1352716312,
    发表于 06-21 16:58

    数据结构

    数据的逻辑结构分为线性结构和非线性结构非空的线性结构
    发表于 03-04 14:13

    常见的数据结构

    顺序表结构的底层实现借助的就是数组,因此对于初学者来说,可以把顺序表完全等价为数组,但实则不是这样。数据结构是研究数据存储方式的门学科,它
    发表于 05-10 07:58

    GPIB命令的数据结构

    针对GPIB命令的结构,提出种存储GPIB命令的数据结构。根据GPIB命令的层次关系的特点,选择数据结构中“树”的概念来存储GPIB命令结点;
    发表于 02-10 16:20 70次下载

    GPIB命令的数据结构

    针对GPIB命令的结构,提出种存储GPIB命令的数据结构。根据GPIB命令的层次关系的特点,选择数据结构中“树”的概念来存储GPIB命令结点;
    发表于 01-04 10:13 0次下载

    数据结构是什么_数据结构有什么用

    数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在种或多种特定关系的数据元素的集合。通常情况下,精心选择的
    发表于 11-17 14:45 1.6w次阅读
    <b class='flag-5'>数据结构</b>是什么_<b class='flag-5'>数据结构</b>有什么用

    什么是数据结构?为什么要学习数据结构数据结构的应用实例分析

    本文档的主要内容详细介绍的是什么是数据结构?为什么要学习数据结构数据结构的应用实例分析包括了:数据结构在串口通信当中的应用,数据结构在按键
    发表于 09-26 15:45 14次下载
    什么是<b class='flag-5'>数据结构</b>?为什么要学习<b class='flag-5'>数据结构</b>?<b class='flag-5'>数据结构</b>的应用实例分析

    如何解决数据结构设计最大频率栈问题?

    。 力扣第 895 题要求我们实现特殊的数据结构「最大频率栈」,比较有意思,让我们实现下面这两
    的头像 发表于 03-02 11:02 1462次阅读

    数据结构解决滑动窗口问题

    前文用 [单调栈解决三道算法问题]介绍了单调栈这种特殊数据结构,本文写类似的数据结构「单调队列」。 也许这种数据结构的名字你没听过,其
    的头像 发表于 04-19 10:50 722次阅读
    <b class='flag-5'>数据结构</b>解决滑动窗口问题

    如何理解掌握Java数据结构

    Java 数据结构是 Java 程序员必须掌握的重要知识之
    的头像 发表于 06-06 15:53 891次阅读

    NetApp的数据结构是如何演变的

    一数据跨分布式资源进行管理,以实现数据移动的致性和控制,安全、可见性、保护和访问。 本文定义了数据结构及其体系
    发表于 08-25 17:15 0次下载
    NetApp的<b class='flag-5'>数据结构</b>是如何演变的

    epoll的基础数据结构

    先看下 eventpoll 这个数据结构,这个数据结构是我们在调用 epoll_create 之后内核创建的句柄,表示了
    的头像 发表于 11-10 10:20 858次阅读
    epoll的基础<b class='flag-5'>数据结构</b>

    redis数据结构的底层实现

    Redis是种内存键值数据库,常用于缓存、消息队列、实时数据分析等场景。它的高性能得益于其精心设计的数据结构和底层实现。本文将详细介绍Re
    的头像 发表于 12-05 10:14 680次阅读