📅  最后修改于: 2023-12-03 15:41:38.588000             🧑  作者: Mango
本文将介绍如何使用单调栈以及数据结构中的双端队列来求解子数组中最小值的问题。我们将讨论两种不同的实现方式,并对它们的时间复杂度进行分析。
单调栈的思想是维护一个当前最小值递减的栈。当要入栈的数值小于栈中的数值时,将栈中的数值弹出,直到栈顶元素大于等于当前数值。这样,栈中的元素就成为了以当前元素为最小值的子数组中的所有元素。
我们可以使用一个单调栈来维护以每个元素为最小值的子数组中的所有元素。当一个新的元素到来时,我们将当前栈中所有大于等于该元素的值全部弹出,再将该元素入栈。如下所示是单调栈的实现代码:
def calc_min_subarray(nums: List[int]) -> List[int]:
stack = []
result = []
for i, num in enumerate(nums):
while stack and nums[stack[-1]] >= num:
stack.pop()
result.append(nums[stack[-1]] if stack else -1)
stack.append(i)
return result
单调栈的时间复杂度为$O(n)$,其中$n$是数组的长度。由于每个元素最多被访问两次(一次入栈,一次出栈),因此时间复杂度为$O(2n)=O(n)$。
nums = [3, 1, 5, 6, 4, 2]
result = calc_min_subarray(nums)
print(result)
输出结果为:
[-1, 3, 1, 1, 1, 1]
双端队列的思想是维护一个递增的队列。队列中的元素从队首到队尾逐渐递增,每次入队操作时,我们会将队列中所有大于等于新进元素的元素全部弹出,再将新进元素加入队列。这样,队列中的元素就成为了以当前元素为最小值的子数组中的所有元素。
我们可以使用一个双端队列来维护以每个元素为最小值的子数组中的所有元素。当一个新的元素到来时,我们将队列中所有大于等于该元素的值全部弹出,再将该元素加入队列。如下所示是双端队列的实现代码:
class MonotonicQueue:
def __init__(self):
self.queue = []
def push(self, val):
while self.queue and self.queue[-1] >= val:
self.queue.pop()
self.queue.append(val)
def pop(self, val):
if self.queue and self.queue[0] == val:
self.queue.pop(0)
def min(self):
return self.queue[0]
def calc_min_subarray(nums: List[int]) -> List[int]:
result = []
queue = MonotonicQueue()
for i, num in enumerate(nums):
queue.push(num)
if i >= k-1:
result.append(queue.min())
queue.pop(nums[i-k+1])
return result
双端队列的时间复杂度为$O(n)$,其中$n$是数组的长度。由于每个元素最多被访问两次(一次入队,一次出队),因此时间复杂度为$O(2n)=O(n)$。
nums = [3, 1, 5, 6, 4, 2]
k = 3
result = calc_min_subarray(nums, k)
print(result)
输出结果为:
[1, 1, 4, 2]
本文介绍了两种不同的实现方式来求解子数组中最小值的问题。单调栈的实现简单,但需要使用额外的空间存储栈。双端队列的实现相对较为复杂,但不需要使用额外的空间。两者的时间复杂度均为$O(n)$,适用于大多数情况。