Leetcode 146. LRU 缓存

目录

    • 题目描述
    • 解法一:哈希表+链表
    • 解法二:OrderedDict

题目描述

请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

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

解法一:哈希表+链表

这题和数据流之类的题目一样,都是一个套路:用链表来记录数据顺序,用哈希表快速找到链表节点,再根据需求移动节点位置。这里用一个单向链表来实现,哈希表存储key对应value节点的前一个节点。当然也可以直接用双向链表。

class ListNode:
    def __init__(self, key, value, next=None):
        self.key = key
        self.value = value
        self.next = next


class LRUCache:
    """解法1:HashMap + LinkedList"""
    def __init__(self, capacity: int):
        self.dummy = ListNode(-1, -1)
        self.tail = self.dummy
        self.key_to_prev = {}
        self.capacity = capacity

    def get(self, key: int) -> int:
        if key not in self.key_to_prev:
            return -1
        self.remove_to_last(key)
        return self.key_to_prev.get(key).next.value

    def put(self, key: int, value: int) -> None:
        if key in self.key_to_prev:
            self.remove_to_last(key)
            self.tail.value = value
            return
        if len(self.key_to_prev) < self.capacity:
            self.add_key_value(key, value)
            return
            # not enough capacity
        self.remove_lru()
        self.add_key_value(key, value)

    def set_value(self, key, value):
        pre_node = self.key_to_prev.get(key)
        target_node = pre_node.next
        target_node.value = value

    def add_key_value(self, key, value):
        new_node = ListNode(key, value)
        self.key_to_prev[key] = self.tail  # s
        self.tail.next = new_node
        self.tail = new_node

    def remove_lru(self):
        remove_target = self.dummy.next
        self.key_to_prev.pop(remove_target.key)
        self.dummy.next = remove_target.next
        # capacity 可能为1,所以还是要判断target.next是否为空
        if remove_target == self.tail:
            self.tail = self.dummy
        else:
            self.key_to_prev[remove_target.next.key] = self.dummy

    def remove_to_last(self, key):
        pre = self.key_to_prev[key]
        target = pre.next
        if self.tail == target:
            return
        pre.next = target.next

        self.key_to_prev[target.next.key] = pre
        target.next = None
        self.tail.next = target
        self.key_to_prev[key] = self.tail
        self.tail = target

解法二:OrderedDict

直接使用内置的数据结构:OrderedDict。

class LRUCache:
    """
    解法2:OrderedDict
    """
    def __init__(self, capacity: int):
        self.cache = OrderedDict()
        self.capacity = capacity

    def get(self, key: int) -> int:
        if key not in self.cache:
            return -1
        value = self.cache.pop(key)  # 访问了, 先删除
        self.cache[key] = value  # 再加到最后
        return value

    def put(self, key: int, value: int) -> None:
        if key in self.cache:
            self.cache.pop(key)  # 先删除
        self.cache[key] = value  # 再加到最后
        if len(self.cache) > self.capacity:
            self.cache.popitem(last=False)

你可能感兴趣的:(LeetCode解题目录,缓存,leetcode,算法)