📌  相关文章
📜  计算左侧和右侧至少有一个较小元素的数组元素(1)

📅  最后修改于: 2023-12-03 15:12:02.485000             🧑  作者: Mango

计算左侧和右侧至少有一个较小元素的数组元素

在处理数组问题时,经常需要计算数组中左侧和右侧至少有一个较小元素的元素个数。这个问题可以用单调栈来解决,时间复杂度为 O(n)。

单调栈是一种后进先出的数据结构。它会维护一个单调递增或单调递减的栈。

在这个问题中,我们使用单调递增栈。当遍历到数组元素 i 时,我们需要找到左侧比 i 小的元素位置 left,以及右侧比 i 小的元素位置 right。我们可以从左到右遍历数组,使用单调递增栈维护左侧比 i 小的元素位置。

当遍历到 i 时,我们需要将单调递增栈中所有比 i 小的元素弹出,直到栈顶元素比 i 大。弹出栈顶元素时,我们可以统计其左右两侧至少有一个较小元素的元素个数。对于弹出的每个元素,其右侧至少有一个较小元素的位置是 i,而其左侧至少有一个较小元素的位置是它在栈中的下一个元素。

当我们将单调递增栈中所有比 i 小的元素都弹出后,将 i 入栈。此时,i 左侧至少有一个较小元素的位置是栈中倒数第二个元素位置(如果栈中只有一个元素,则 i 左侧无较小元素),右侧至少有一个较小元素的位置是 i。

代码实现如下:

def count_elements_with_smaller_neighbors(arr):
    left_smaller = [0] * len(arr)
    right_smaller = [0] * len(arr)
    stack = []
    for i in range(len(arr)):
        while stack and arr[stack[-1]] > arr[i]:
            j = stack.pop()
            right_smaller[j] = i
            if stack:
                left_smaller[j] = stack[-1]
        stack.append(i)
    while stack:
        j = stack.pop()
        if stack:
            left_smaller[j] = stack[-1]
    return sum(l < r for l, r in zip(left_smaller, right_smaller))

在这个实现中,我们使用了两个辅助数组 left_smaller 和 right_smaller。这两个数组用于保存每个数组元素左侧和右侧至少有一个较小元素的位置。我们使用单调递增栈维护 left_smaller 和 right_smaller。

在函数返回之前,我们使用 zip 函数和列表解析计算左侧和右侧都有至少一个较小元素的数组元素个数。

这个算法的时间复杂度为 O(n),空间复杂度为 O(n)。

总结:单调栈是一个非常强大的工具。我们可以用它解决很多数组问题。在使用单调栈时,我们需要非常熟练掌握栈的操作,以及如何使用栈来维护递增或递减的序列。