LeetCode | 一文帮你搞定“最小栈和LRU_cache”(python版,详细讲解)

数据结构设计 | 最小栈和LRU_cache

数据结构设计类的问题,在面试过程中作为考察编码者的基本功经常被问到,这类题目在leetcode里面数量不多。

最小栈

  • leetcode155 Minstack

Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.

push(x) – Push element x onto stack.
pop() – Removes the element on top of the stack.
top() – Get the top element.
getMin() – Retrieve the minimum element in the stack.

Example:

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> Returns -3.
minStack.pop();
minStack.top();      --> Returns 0.
minStack.getMin();   --> Returns -2.

这道最小栈跟原来的栈相比就是多了一个功能,可以返回该栈的最小值。

解题思路,使用两个栈实现。
一个栈来按顺序存储 push 进来的数据,另一个用来存出现过的最小值。

class MinStack:
    
    def __init__(self):
        self.stack = []
        self.min_stack = []

    """
    @param: number: An integer
    @return: nothing
    """
    def push(self, number):
        self.stack.append(number)
        if len(self.min_stack) == 0:
            self.min_stack.append(number)
        else:        
            if number > self.min_stack[-1]:
                self.min_stack.append(self.min_stack[-1])
            else:
                self.min_stack.append(number)
                
    """
    @return: An integer
    """
    def pop(self):
        self.min_stack.pop()
        return self.stack.pop()

    """
    @return: An integer
    """
    def min(self):
        return self.min_stack[-1]

缓存设计

  • leetcode146 LRUcache

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

The cache is initialized with a positive capacity.

LRUCache cache = new LRUCache( 2 /* capacity */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // returns 1
cache.put(3, 3);    // evicts key 2
cache.get(2);       // returns -1 (not found)
cache.put(4, 4);    // evicts key 1
cache.get(1);       // returns -1 (not found)
cache.get(3);       // returns 3
cache.get(4);       // returns 4

题目的意思是实现LRUCache算法。
LRUCache全名为Least Recently Used,即最近最少使用算法,是操作系统中发生缺页中断时常用的一种页面置换算法。

对于LRU算法主要实现两个操作:

  • 访问数据块
    • 将访问的数据块更新为最近访问,并返回访问的数据块。
  • 添加数据块
    • 如果Cache还有容量,将添加的数据块添加到Cache之后标记为最近访问。
    • 如果Cache的容量已满,替换最久未访问的数据块为添加的数据块。

由于放入的元素是(key-value),很自然想到要用hash表(字典)。
由于要定义最久未访问的数据块,并很快地实现元素的替换操作,可以使用链表list,list头结点放最近访问的数据,list尾结点放最久未访问的数据.

set

如果新来一个set请求, 我们先去查hash表

  • 如果已经存在了这个key, 那么我们需要更新其value, 然后将其在list的结点位置移动到链表首部(设为最近访问)。
  • 如果不存在这个key, 那么我们需要在hash表和链表中都添加这个值, 并且如果添加之后链表长度超过最大长度, 我们需要将链表尾部的节点删除(最久未访问), 并且删除其在hash表中的记录。

get

如果新来get请求,则也先去查hash表。

  • 如果已经存在了这个key, 那么我们返回value, 然后将其在list的结点位置移动到链表首部(设为最近访问)。
  • 不存在key,返回-1。

另外一个非常关键的点是,在链表中查找一个节点的时间复杂度是O(n), 所以当移动一个节点到链表首部的时候,如果直接查找就是O(n);但是,如果使用hash表保存每个key对应的节点,那么就可以在O(1)的时间内移动结点到链表首部。

# 由于要删除最后一个节点,需要定义双向链表
class ListNode(object):
    def __init__(self, key, val, prev_node = None, next_node = None):
        self.key = key
        self.val = x # node的值为key对应的value
        self.prev = prev_node 
        self.next_node = next_node

class DoubleLinkList(object):
    def __init__(self, capacity):
        self.capacity = capacity
        self.size = 0
        self.head = None
        self.tail = None

    def append(self, node):
        if self.size == self.capacity:
            raise ValueError("The double link list has been full")

        self.size += 1

        if self.head is None:
            self.head = self.tail = Node
            return

        # 在链表头部加入node
        node.next = self.head
        self.head.prev = node

        self.head = node

    def delete(self, node):
        if self.size == 0:
            raise ValueError("The DoubleLinkList has been empty")

        self.size -= 1

        if node == self.head:
            temp_node = node.next
            if temp_node:
                temp_node.prev = None
                node.next = None
            self.head = temp_node

        elif node == self.tail:
            temp_node = node.prev
            if temp_node:
                temp_node.next = None
                node.prev = None
            self.tail = temp_node

        else:
            node.prev.next = node.next
            node.next.prev = node.prev

        return node

class LRUCache(object):

    def __init__(self, capacity):
        self.capacity = capacity
        self.cache = dict()
        self.double_list = DoubleLinkList(self.capacity)

    def get(self, key):
        if key not in self.cache:
            return -1

        cur_node = self.cache[key]

        self.double_list.delete(cur_node)
        self.double_list.append(cur_node)

        return cur_node.val

    def put(self, key, value):
        if key in self.cache:
            cur_node = self.cache[key]
            cur_node.val = value

            self.double_list.delete(cur_node)
            self.double_list.append(cur_node)

        else:
            # 找不到key则加入
            if self.double_list.size == self.capacity:
                # 删除最后节点
                tail_node = self.double_list.delete(self.double_list.tail)
                del self.cache[tail_node.key]

            # 加入新节点
            new_node = ListNode(key, value)
            self.double_list.append(new_node)
            self.cache[key] = new_node

更多精彩文章,欢迎关注公众号“Li的白日呓语”。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值