leetcode 面试题 16.25. LRU 缓存,146. LRU 缓存机制,链表头插法,删除,含测试用例, 牛客网LRU缓存

题目来源:力扣(LeetCode)和牛客网在线编程题
链接:https://leetcode-cn.com/problems
https://www.nowcoder.com/activity/oj
特别鸣谢:来自夸夸群的 醉笑陪公看落花@知乎王不懂不懂@知乎QFIUNE@csdn
感谢任老师倾囊相授,感谢小伙伴们督促学习,一起进步

难度中等

面试题 16.25. LRU 缓存/146. LRU 缓存机制

二者是同一道题

设计和构建一个“最近最少使用”缓存,该缓存会删除最近最少使用的项目。缓存应该从键映射到值(允许你插入和检索特定键对应的值),并在初始化时指定最大容量。当缓存被填满时,它应该删除最近最少使用的项目。

它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。

示例:

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得密钥 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得密钥 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lru-cache-lcci

在这里插入图片描述

解题思路tips:

  • 双向循环链表
  • 头插法
  • 记录头尾结点
  • 控制链表长度 count 和 capacity
  • 每次访问(put or get)之后, 在原队列上删除,然后再把这个节点插入头部
  • 每次 put之后, 键已经存在的情况下,需要更新值
  • 链表达到最长长度时,删除尾节点的pre
  • 字典, 通过键快速访问链表中的节点
  • 节点里面需要存key,删除尾节点的pre的时候,可以查询到key值,从而把这个key从字典里面删掉
  • 访问的节点 == header.next , 不用在队列上删除了再插入

解题代码

# Title     : lc16_25_LRU缓存.py
# Created on: 2021/7/16 22:51

class Node:
    def __init__(self,k,v):
        self.k = k
        self.v = v
        self.next = None
        self.pre = None

class LRUCache:

    def __init__(self, capacity: int):
        self.capacity = capacity
        self.containers = {}
        self.count = 0
        self.header = Node(None,0)
        self.tail = Node(None,0)

        self.header.next = self.tail
        self.tail.pre = self.header


    def get(self, key: int) -> int:
        if key in self.containers.keys():
            node = self.containers[key]
            if self.header.next == node:return self.containers[key].v
            self.delete(node)
            node = self.insert(node)
            self.containers[key] = node
            return self.containers[key].v
        else:return -1

    def put(self, key: int, value: int) -> None:
        node = Node(key, value)
        if key in self.containers.keys():
            old_node = self.containers[key]
            self.delete(old_node)
            node = self.insert(node)
        else:
            if self.count <self.capacity:
                self.count += 1
            else:
                # delete node near tail
                delete_node = self.delete(self.tail.pre)
                self.containers.pop(delete_node.k)
            node = self.insert(node)
        self.containers[key] = node

        pass
    def delete (self,node:Node):
        node.pre.next = node.next
        node.next.pre = node.pre
        return node
    def insert(self,node:Node):
        if not self.header.next: # empyty
            self.header.next = node
            node.pre = self.header
        else:
            node.next = self.header.next
            node.pre = self.header
            self.header.next = node
            node.next.pre = node
        return node

#
if __name__ == '__main__':
    opr =  ["LRUCache", "put", "put", "get", "put", "put", "get"]
    data = [[2], [2, 1], [2, 2], [2], [1, 1], [4, 1], [2]]
    # [null,null,null,2,null,null,-1]
    capacity = data[0][0]
    obj = LRUCache(capacity)
    print(None,end=',')
    for idx in range(1,len(data)):
        if opr[idx] == 'put': print(obj.put(data[idx][0],data[idx][1]),end=',')
        else:
            print(obj.get(data[idx][0]),end=',')



牛客网 LRU缓存 解题代码

设计LRU缓存结构,该结构在构造时确定大小,假设大小为K,并有如下两个功能
set(key, value):将记录(key, value)插入该结构
get(key):返回key对应的value值
[要求]
set和get方法的时间复杂度为O(1)
某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的。
当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。
若opt=1,接下来两个整数x, y,表示set(x, y)
若opt=2,接下来一个整数x,表示get(x),若x未出现过或已被移除,则返回-1
对于每个操作2,输出一个答案

输入:[[1,1,1],[1,2,2],[1,3,2],[2,1],[1,4,4],[2,2]],3
返回值:[1,-1]

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

同上题,只是需要手动处理一下输入信息,修改一下入口参数即可

# Title     : lc16_25_LRU缓存.py
# Created on: 2021/7/16 22:51
#
# lru design
# @param operators int整型二维数组 the ops
# @param k int整型 the k
# @return int整型一维数组
#

class Node:
    def __init__(self, k, v):
        self.k = k
        self.v = v
        self.next = None
        self.pre = None


class Solution:
    def __init__(self):
        self.capacity = None
        self.containers = {}
        self.count = 0
        self.header = Node(None, 0)
        self.tail = Node(None, 0)

        self.header.next = self.tail
        self.tail.pre = self.header

    def LRU(self, operators, k):
        self.capacity = k
        # write code here
        ans = []
        for opr in operators:
            if opr[0] == 1:
                self.set(opr[1], opr[2])
            else:
                ans.append(self.get(opr[1]))
        return ans

    def get(self, key: int) -> int:
        if key in self.containers.keys():
            node = self.containers[key]
            if self.header.next == node: return self.containers[key].v
            self.delete(node)
            node = self.insert(node)
            self.containers[key] = node
            return self.containers[key].v
        else:
            return -1

    def set(self, key: int, value: int) -> None:
        node = Node(key, value)
        if key in self.containers.keys():
            old_node = self.containers[key]
            self.delete(old_node)
            node = self.insert(node)
        else:
            if self.count < self.capacity:
                self.count += 1
            else:
                # delete node near tail
                delete_node = self.delete(self.tail.pre)
                self.containers.pop(delete_node.k)
            node = self.insert(node)
        self.containers[key] = node

    def delete(self, node: Node):
        node.pre.next = node.next
        node.next.pre = node.pre
        return node

    def insert(self, node: Node):
        node.next = self.header.next
        node.pre = self.header
        self.header.next = node
        node.next.pre = node
        return node


#
if __name__ == '__main__':
    # 输入:
    # [[1,1,1],[1,2,2],[1,3,2],[2,1],[1,4,4],[2,2]],3
    # 返回值:
    # [1,-1]
    operators = [[1,1,1],[1,2,2],[1,3,2],[2,1],[1,4,4],[2,2]]
    k = 3
    ans = Solution().LRU(operators, k)
    print(ans)

在这里插入图片描述

解题草稿

在这里插入图片描述

缓存机制在递归函数中的应用

在leetcode 32题中,零钱凑整中

带备忘录的递归代码

class Solution:
    mydict = {}
    def coinChange(self, coins,amount):
        self.coins = coins
        self.mydict = {} # 备忘录,记住算过的金额

        return self.dp(amount)

    def dp(self,amount):
        if amount == 0:
            return 0
        elif amount < 0:
            return -1
        if amount in self.mydict:return self.mydict[amount]
        min_tp = sys.maxsize
        for c in self.coins:
            tp = self.dp(amount - c) + 1
            if (tp > 0): min_tp = min(tp, min_tp)
        ans = -1 if min_tp == sys.maxsize else min_tp
        self.mydict[amount] = ans #  # 用备忘录记录
        return ans

用functools.lru_cache()记录缓存 代码

import functools
import sys
class Solution:
    def coinChange(self, coins,amount):
        self.coins = coins
        @functools.lru_cache()  # 加上缓存之后,就消除了递归中的重复计算
        def dp(amount):
            if amount==0:return 0
            elif amount<0:return -1
            min_tp = sys.maxsize
            for c in coins:
                tp = dp(amount-c)+1
                if (tp>0):min_tp=min(tp,min_tp)
            return -1 if min_tp==sys.maxsize else min_tp
        return dp(amount)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值