【力扣日记】面试题59 - II. 队列的最大值 | 数据结构设计

题目描述

请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的时间复杂度都是O(1)。

若队列为空,pop_front 和 max_value 需要返回 -1

算法思路

NAIVE

class MaxQueue:

    def __init__(self):
        self.ls=[]

    def max_value(self) -> int:
        if not self.ls:return -1
        return max(self.ls)

    def push_back(self, value: int) -> None:
        self.ls.append(value)

    def pop_front(self) -> int:
        if not self.ls:return -1
        else:return self.ls.pop(0)

直接使用python内置数据结构列表来完成,都不满足题目要求的时间复杂度,
NAIVE版本的pop和max都是O(n)级别。
纯属偷鸡。不提倡

执行用时 :272 ms, 在所有 Python3 提交中击败了64.64%的用户
内存消耗 :17 MB, 在所有 Python3 提交中击败了100.00%的用户

IMPROVE

push_back 和 pop_front 的时间复杂度都是O(1),恕我才疏学浅,列表是没办法实现push和pop都做到时间复杂度O(1)的吧?所以只能去评论区打开思路了,话说回来居然有人看不懂这次的题,也是比较神奇。

这里参见参考11的思路:使用辅助数组

再提一句,python里的deque数据结构可以实现pop(0)的时间复杂度为O(1),这里先不展开谈。

只考虑辅助数组。
对数组队列queue,直接先进先出即可。
对辅助数组queue_sort,要确保数组最大值在队列首位,那么如何做到?

在push值的过程中,维护辅助数组。

while sort_que and sort_que[-1] < value:
    sort_que.pop()
sort_que.append(value)

即push进的value要满足小于sort_que[-1],或辅助列表为空,这样才能把value放入辅助数组。

其实这样说也可能不好理解,那么:
在这里插入图片描述

from collections import deque
class MaxQueue:

    def __init__(self):
        self.queue=deque()
        self.queue_sort=deque()

    def max_value(self) -> int:
        if not self.queue_sort:return -1
        return self.queue_sort[0] if self.queue_sort else -1


    def push_back(self, value: int) -> None:
        self.queue.append(value)
        while self.queue_sort and self.queue_sort[-1]<value:
            self.queue_sort.pop()
        self.queue_sort.append(value)

    def pop_front(self) -> int:
        if not self.queue:return -1
        value=self.queue.popleft()
        if value==self.queue_sort[0]:self.queue_sort.popleft()
        return value

执行用时 :268 ms, 在所有 Python3 提交中击败了68.78%的用户
内存消耗 :17 MB, 在所有 Python3 提交中击败了100.00%的用户

看到这个执行时间有点尴尬。

至于时间复杂度的问题。

deque使得pop(0)的时间复杂度为O(1)。相关参考:deque详解
而push方法中对辅助数组的操作,因为所有的元素都只会进出一次辅助数组,所以平均时间复杂度是O(1)。

deque详解2

本来想找到deque的源代码的,结果这是用CPython的C代码实现的,集合python模块只是导入了该名称。名为_collections.so或的文件,_collectionsmodule.so该文件具有真正的实现,但是我没找到。

退而求其次,只求弄明白操作方式。于是在官方网站找到了详情。

class collections.deque([iterable[, maxlen]])

返回一个新的双向队列对象,从左到右初始化(用方法 append()) ,从 iterable (迭代对象) 数据创建。如果 iterable 没有指定,新队列为空。

Deque 支持线程安全,内存高效添加(append)和弹出(pop),从两端都可以,两个方向的大概开销都是 O(1) 复杂度。

如果 maxlen 没有指定或者是 None ,deques 可以增长到任意长度。否则,deque就限定到指定最大长度。一旦限定长度的deque满了,当新项加入时,同样数量的项就从另一端弹出。
——————
基础方法详情:

方法 解释 备注
append(x) 添加 x 到右端。
appendleft(x) 添加 x 到左端。
clear() 移除所有元素,使其长度为0.
copy() 创建一份浅拷贝。 3.5 新版功能.
count(x) 计算 deque 中元素等于 x 的个数。 3.2 新版功能.
extend(iterable) 扩展deque的右侧,通过添加iterable参数中的元素。
extendleft(iterable) 扩展deque的左侧,通过添加iterable参数中的元素。 左添加时,在结果中iterable参数中的顺序将被反过来添加。
index(x[, start[, stop]]) 返回 x 在 deque 中的位置(索引 start 到stop 之间)。 返回第一个匹配项,如果未找到则引发 ValueError。 3.5 新版功能.
insert(i, x) 在位置 i 插入 x 。 如果插入会导致一个限长 deque 超出长度 maxlen 的话,就引发一个 IndexError。3.5 新版功能.
pop() 移去并且返回一个元素,deque 最右侧的那一个。 如果没有元素的话,就引发一个 IndexError。
popleft() 移去并且返回一个元素,deque 最左侧的那一个。 如果没有元素的话,就引发 IndexError。
remove(value) 移除找到的第一个 value。 如果没有的话就引发 ValueError。
reverse() 将deque逆序排列。返回 None 。 3.2 新版功能.
rotate(n=1) 向右循环移动 n 步。 如果 n 是负数,就向左循环。 deque非空时,向右循环移动一步等价 d.appendleft(d.pop()) ,左循环一步等价 d.append(d.popleft()) 。

参考


  1. @ QuantumDrive ↩︎

  2. python/collections/deque ↩︎

发布了210 篇原创文章 · 获赞 20 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Heart_for_Ling/article/details/104713037