返回

数据结构-单调队列优化之队列的最大值

前端

队列回顾
一个队列,又名先进先出队列,是一种遵循先进先出(FIFO)原则的数据结构。队列中的元素按照它们进入队列的顺序排列。元素只能从队列的头部(front)进入,并且只能从队列的尾部(rear)移除。队列的典型操作包括:

  • enqueue(item):将新元素添加到队列的尾部。
  • dequeue():从队列的头部删除并返回元素。
  • size():返回队列中元素的数量。
  • isEmpty():检查队列是否为空。
  • peek():返回队列的头部元素,但不删除它。

队列通常用数组或链表实现。数组实现简单,但可能需要进行昂贵的重新分配操作来处理队列的增长和收缩。链表实现更灵活,但它的性能可能不如数组实现好。

单调队列简介

单调队列是一种特殊的队列,它满足单调性条件,即队列中的元素按某种顺序排列(升序或降序)。单调队列通常用于解决各种数据结构问题,例如求滑动窗口的最大值、最小值等。

如何实现单调队列?

一种简单的方法是使用两个队列来模拟单调队列。一个队列用于存储元素,另一个队列用于存储最大值(或最小值)。当一个新元素进入单调队列时,它被添加到存储元素的队列中。同时,如果新元素比存储最大值(或最小值)的队列中的最大值(或最小值)更大(或更小),则它也被添加到存储最大值(或最小值)的队列中。

当从单调队列中删除一个元素时,它从存储元素的队列中删除。同时,如果被删除的元素是存储最大值(或最小值)的队列中的最大值(或最小值),则它也被从存储最大值(或最小值)的队列中删除。

单调队列的时间复杂度

单调队列的均摊时间复杂度为O(1)。这是因为,在最坏的情况下,单调队列的操作需要O(n)的时间,其中n是队列中的元素数量。然而,在大多数情况下,单调队列的操作只需要O(1)的时间。

用单调队列优化队列的最大值

我们现在来看一个经典的问题:队列的最大值

问题

给定一个整数数组nums和一个窗口大小k,您需要找到 nums 所有连续子数组的最大值。

例如,对于 nums = [1, 3, -1, -3, 5, 3, 6, 7] 和 k = 3,您需要找到所有长度为 3 的连续子数组的最大值。结果是 [3, 3, 5, 5, 6, 7]。

解题思路:

这个问题可以用单调队列来优化。具体步骤如下:

  1. 初始化一个单调队列,并将第一个窗口中的元素加入队列中。
  2. 从第二个窗口开始,对于每个窗口:
    • 将当前窗口的右边界元素加入队列中。
    • 如果队列的头部元素不是当前窗口的左边界元素,则将队列的头部元素弹出。
    • 将队列的头部元素作为当前窗口的最大值。
  3. 将所有窗口的最大值输出。

算法复杂度:

该算法的时间复杂度为O(n),其中n是nums数组的长度。这是因为,每个元素最多被加入和弹出队列一次。

Python代码实现:

def maxSlidingWindow(nums, k):
    """
    :type nums: List[int]
    :type k: int
    :rtype: List[int]
    """
    if not nums or k <= 0 or len(nums) < k:
        return []

    # 初始化单调队列和结果列表
    queue = []
    result = []

    # 将第一个窗口中的元素加入队列中
    for i in range(k):
        while queue and nums[i] > nums[queue[-1]]:
            queue.pop()
        queue.append(i)

    # 从第二个窗口开始,对每个窗口进行处理
    for i in range(k, len(nums)):
        # 将当前窗口的右边界元素加入队列中
        while queue and nums[i] > nums[queue[-1]]:
            queue.pop()
        queue.append(i)

        # 如果队列的头部元素不是当前窗口的左边界元素,则将队列的头部元素弹出
        if queue[0] <= i - k:
            queue.pop(0)

        # 将队列的头部元素作为当前窗口的最大值
        result.append(nums[queue[0]])

    return result

总结

在本文中,我们介绍了单调队列这一重要的数据结构,并通过队列的最大值问题展示了它的应用。通过使用单调队列,我们可以在O(n)的时间复杂度内解决这一问题。

单调队列的应用非常广泛,除了队列的最大值问题之外,它还可用于解决各种数据结构问题,例如求滑动窗口的最大值、最小值、中位数等。掌握单调队列的使用方法将帮助您解决更多复杂的数据结构问题。