LeetCode 146. LRU Cache - 链表(Linked List)系列题29

Design a data structure that follows the constraints of a Least Recently Used (LRU) cache.

Implement the LRUCache class:

  • LRUCache(int capacity) Initialize the LRU cache with positive size capacity.
  • int get(int key) Return the value of the key if the key exists, otherwise return -1.
  • void put(int key, int value) Update the value of the key if the key exists. Otherwise, add the key-value pair to the cache. If the number of keys exceeds the capacity from this operation, evict the least recently used key.

The functions get and put must each run in O(1) average time complexity.

Example 1:

Input
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
Output
[null, null, null, 1, null, -1, null, -1, 3, 4]

Explanation
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // cache is {1=1}
lRUCache.put(2, 2); // cache is {1=1, 2=2}
lRUCache.get(1);    // return 1
lRUCache.put(3, 3); // LRU key was 2, evicts key 2, cache is {1=1, 3=3}
lRUCache.get(2);    // returns -1 (not found)
lRUCache.put(4, 4); // LRU key was 1, evicts key 1, cache is {4=4, 3=3}
lRUCache.get(1);    // return -1 (not found)
lRUCache.get(3);    // return 3
lRUCache.get(4);    // return 4

Constraints:

  • 1 <= capacity <= 3000
  • 0 <= key <= 104
  • 0 <= value <= 105
  • At most 2 * 105 calls will be made to get and put.

题目要求设计一个LRU(Least Recently Used) Cache,中文叫最近最少使用缓存。做这题之前可以找一本操作系统的书先了解一下LRU。其核心思想就是当缓存满了的时候,插入新内容之前需要先丢弃最近最少使用的那个单元的内容(即缓存里最长时间没被读或写的那个单元)。如果某一个单元刚被读或写过,那么该单元就变成了缓存里最近使用过的(Most Recently Used)。

要求实现如下接口函数:

  • LRUCache(int capacity) 初始化缓存大小,以及实现LRU所需的数据结构和变量。
  • int get(int key) 给定一个key,如存在返回对应的value,如不存在返回-1,该key-value所在的单元变成最近使用过的(Most Recently Used)
  • void put(int key, int value) 更新key对应的value,如key已存在则更新value;如key不存在则插入一个key-value新单元,要是缓存已满则先把最近最少使用least recently used的key-value丢弃。刚被更新过的或者新插入的key-value单元就变成最近使用过的Most Recently Used。

要求 get()和put()的时间复杂度都O(1)。

根据题目分析双向链表是最合适的数据结构。每个节点存储一对key-value,最近使用的在链表头,最近最少使用的在链表尾。对于一个新节点则直接插入到链表头因为它本身就是the most recently used。对于链表里已存在的任意一个key,只要它对应的节点被读或写之后就变成了the most recently used,因此需要从当前位置删除重新插入到链表头。另外,为了在O(1)时间内得到key对应的节点,需要借助一个字典dict: 从key映射到节点node。

class ListNode:
    def __init__(self, key = None, val = None):
        self.key = key
        self.val = val
        self.prev = None
        self.next = None
        
class LRUCache:

    def __init__(self, capacity: int):
        self.head = ListNode()
        self.tail = ListNode()
        self.head.next = self.tail
        self.tail.prev = self.head
        self.cnt = 0
        self.size = capacity
        self.v2n = {}

    def get(self, key: int) -> int:
        if key not in self.v2n:
            return -1
        node = self.v2n[key]
        self.delete(node)
        self.insert(node)
        return node.val

    def put(self, key: int, value: int) -> None:
        node = None
        if key in self.v2n:
            node = self.v2n[key]
            node.val = value
            self.delete(node)
        else:
            node = ListNode(key, value)
            if self.cnt == self.size:
                self.v2n.pop(self.tail.prev.key)
                self.delete(self.tail.prev)
                self.cnt -= 1
            
            self.cnt += 1
            self.v2n[key] = node
        self.insert(node)
        

    def insert(self, node):
        self.head.next.prev = node
        node.next = self.head.next
        node.prev = self.head
        self.head.next = node
    
    def delete(self, node):
        node.next.prev = node.prev
        node.prev.next = node.next
        

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值