返回

你所不知道的单调栈与单调队列(下)

前端

在高速发展的技术领域中,不断更新的算法和数据结构层出不穷,对于技术从业者而言,掌握这些工具是至关重要的。单调栈和单调队列作为两个重要的数据结构,因其出色的特性而受到广泛应用。在这篇博文中,我们将深入探讨单调栈和单调队列,揭开它们鲜为人知的一面。

单调栈的本质

单调栈是一种遵循单调性原则的数据结构,即栈中元素的某个属性值(例如,数字大小)始终保持递增或递减的顺序。单调栈的出栈操作从栈顶(即最近添加的元素)开始,这与普通栈遵循的后进先出(LIFO)原则不同。

通过利用单调性,单调栈可以高效地解决一系列问题,例如:

  • 求解矩形面积的最大值
  • 寻找最近的较大元素
  • 判定数组是否可以被重新排列成一个山脉数组

单调队列的奥秘

单调队列与单调栈类似,但其遵循的单调性原则适用于队列中元素的属性值。单调队列中的元素按照某种顺序排列,例如从队头到队尾递增或递减。与单调栈不同,单调队列的出队操作从队头开始,而入队操作在队尾进行。

单调队列的强大之处在于它的以下应用:

  • 计算滑动窗口的最大值或最小值
  • 判定数组是否可以被重新排列成一个谷底数组
  • 求解最长连续子数组和

理解关键区别

虽然单调栈和单调队列都遵循单调性原则,但它们在出队方式上存在根本区别。单调栈的出栈操作从栈顶开始,而单调队列的出队操作从队头开始。这种差异导致了两者在具体应用场景上的不同。

此外,单调栈通常用于解决与最近元素或最大/最小值相关的操作,而单调队列则更适用于处理滑动窗口和数组排列等问题。

代码示例:求解滑动窗口的最大值

from collections import deque

def max_sliding_window(arr, k):
    """
    :param arr: 输入数组
    :param k: 滑动窗口大小
    :return: 滑动窗口最大值的列表
    """

    # 创建单调队列,存储数组元素索引
    q = deque()

    # 存储滑动窗口最大值的列表
    max_values = []

    for i in range(len(arr)):
        # 如果队尾元素已不在窗口内,则弹出
        while q and i - q[0] >= k:
            q.popleft()

        # 如果当前元素比队尾元素大,则弹出队尾元素
        while q and arr[i] > arr[q[-1]]:
            q.pop()

        # 入队
        q.append(i)

        # 如果窗口大小已达到k,则将队首元素作为最大值
        if len(q) == k:
            max_values.append(arr[q[0]])

    return max_values

结语

单调栈和单调队列是计算机科学中至关重要的数据结构,它们的特性使它们能够高效地解决一系列问题。通过理解它们的核心原理和应用场景,技术人员可以利用这些工具增强他们的算法设计能力。