题解 | #设计LRU缓存结构#

设计LRU缓存结构

http://www.nowcoder.com/practice/e3769a5f49894d49b871c09cadd13a61

思路

题目分析

  1. 题目希望我们设计一个LRU规则来处理<key,value>数据
  2. 对于一个数据结构,LRU规则在该结构上的要求是:
    • 该数据结构有一个最大的容量值,可储存的数据不超过这个值
    • 对于set操作,该数据结构目的就是将<key,value>存入数据结构中,并标记其为最近最新访问
    • 对于get操作,该数据结构目的就是对其要求访问的key返回对应的value
  3. 题目中给出了二维数组,其中对于每一个子数组opt[]opt[0] == 1表示要执行set操作,opt[0] == 2表示要执行get操作。
  4. 在设计结构的时候还要兼顾是否越界、是否有同key不同value等细节问题
  • 首先python中集成了结合哈希表和双向链表的数据结构OrderedDict,在Java中同样有类似的结构LinkedHashMap,有很多方便的接口可以让我们使用。Python的用例方案见方法一。
  • 然后该题具体分析就要求我们采用自己建立这种数据结构的方式。
    • 我们当遇到操作是set时:
      • 首先判断key是否在哈希表中
        • 如果在哈希表中则根据哈希表读取结点,然后将结点的val值更新并移动到双向链表首位
        • 如果不在哈希表中则新建一个节点插入到双向链表首位,更新当前size,然后判断是否超过了最大容量,超过容量则退出末尾的结点,并在哈希表中删除它。
    • 我们当遇到操作是get时:
      • 首先判断key是否在哈希表中
        • 如果key在哈希表中则读取其val值到返回列表中,并且更新其位置到双向链表首位
        • 如果key不在哈希表中则返回-1到返回列表中

图片说明

方法一:使用python内置数据结构

#
# lru design
# @param operators int整型二维数组 the ops
# @param k int整型 the k
# @return int整型一维数组
#
import collections
class Solution:
    def LRU(self , operators , k ):
        # write code here
        res = []
        d = collections.OrderedDict()
        d.capacity = k
        for op in operators:
            if op[0] == 1:                        # 表示要set
                if op[1] in d:                    # 如果当前key在d中
                    d.move_to_end(op[1])          # 则移到末尾
                d[op[1]] = op[2]                  # 更新key,value的值
                if len(d) > k:                    # 如果数量超过了上界则踢出队首的
                    d.popitem(last = False)       # 则踢出最前面的key,value
            else :                                # 表示要get
                if op[1] not in d:                # 如果key不存在
                    res.append(-1)                # 则输出-1
                else:                             # 如果key存在
                    res.append(d[op[1]])          # 输出value
                    d.move_to_end(op[1])          # 并移到末尾
        return res

复杂度分析

  • 时间复杂度:对于哈希+双向链表的内置数据结构,时间复杂度为
  • 空间复杂度:空间上借助了哈希表和双向链表结构,空间复杂度取决于链表长,则为

方法二:手动打造双向列表的结构,配合哈希表使用

#
# lru design
# @param operators int整型二维数组 the ops
# @param k int整型 the k
# @return int整型一维数组
#
# 结点
class DLinkedNode:
    def __init__(self, key = 0, val = 0):
        self.key = key
        self.val = val
        self.prev = None
        self.next = None

# 双向链表
class LRU:
    def __init__(self, capacity:int):
        self.cache = dict()
        # 头结点和尾结点都是伪结点
        self.head = DLinkedNode()
        self.tail = DLinkedNode()
        # 先连接起来首尾
        self.head.next = self.tail
        self.tail.prev = self.head
        # 设定LRU的最大容量
        self.capacity = capacity
        self.size = 0

    # 当opt=2的时候调用get函数
    def get(self, key:int) -> int:
        if key not in self.cache:
            return -1
        # key存在的情况下,首先通过哈希表定位然后将结点移动到头部
        node = self.cache[key]
        # 将结点node取出
        node.prev.next = node.next
        node.next.prev = node.prev
        # 移到首位操作
        node.prev = self.head
        node.next = self.head.next
        node.next.prev = node
        node.prev.next = node
        return node.val

    # 当opt=1的时候调用put函数
    def put(self, key:int, val:int) -> None:
        # 如果key不存在
        if key not in self.cache:
            node = DLinkedNode(key, val)    # 新建一个节点
            self.cache[key] = node          # 放入哈希表中
            node.prev = self.head
            node.next = self.head.next
            node.next.prev = node
            node.prev.next = node            # 添加到双向链表头
            self.size += 1                  # 双向链表的结点数+1
            if self.size > self.capacity:
                # 双向链表的长度大于最大的容量
                node = self.tail.prev
                # 移除最后一个节点
                node.prev.next = node.next
                node.next.prev = node.prev
                self.cache.pop(node.key)
                self.size -= 1
        # key存在则首先通过哈希表找到key,然后修改val,并挪到头部
        else:
            node = self.cache[key]
            node.val = val
            # 将结点node取出
            node.prev.next = node.next
            node.next.prev = node.prev
            # 移动node到首位
            node.prev = self.head
            node.next = self.head.next
            node.next.prev = node
            node.prev.next = node

class Solution:
    def LRU(self , operators , k ):
        # write code here
        res = []
        linklist = LRU(k)
        for opt in operators:
            if opt[0] == 1:
                linklist.put(opt[1], opt[2])
            else:
                res.append(linklist.get(opt[1]))
        return res

复杂度分析

  • 时间复杂度:对于哈希+双向链表的内置数据结构,put方法和get方法的时间复杂度为
  • 空间复杂度:空间上借助了哈希表和双向链表结构,空间复杂度取决于链表长,则为
不会做题写的题解 文章被收录于专栏

哎呀我好笨呀,一不小心又不会了一道题

全部评论

相关推荐

行云流水1971:这份实习简历的优化建议: 结构清晰化:拆分 “校园经历”“实习经历” 板块(当前内容混杂),按 “实习→校园→技能” 逻辑排版,求职意向明确为具体岗位(如 “市场 / 运营实习生”)。 经历具象化:现有描述偏流程,需补充 “动作 + 数据”,比如校园活动 “负责宣传” 可加 “运营公众号发布 5 篇推文,阅读量超 2000+,带动 300 + 人参与”;实习内容补充 “协助完成 XX 任务,效率提升 X%”。 岗位匹配度:锚定目标岗位能力,比如申请运营岗,突出 “内容编辑、活动执行” 相关动作;申请市场岗,强化 “资源对接、数据统计” 细节。 信息精简:删减冗余表述(如重复的 “负责”),用短句分点,比如 “策划校园招聘会:联系 10 + 企业,组织 200 + 学生参与,到场率达 85%”。 技能落地:将 “Office、PS” 绑定经历,比如 “用 Excel 整理活动数据,输出 3 份分析表;用 PS 设计 2 张活动海报”,避免技能单独罗列。 优化后需强化 “经历 - 能力 - 岗位需求” 的关联,让实习 / 校园经历的价值更直观。 若需要进一步优化服务,私信
实习,投递多份简历没人回...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务