python数据结构——单调栈和单调队列
单调栈
数据结构栈遵循先进后出的原则,在python中,利用list就可以实现栈的功能
stack = []
stack.append(1)
stack.append(2)
print(stack.pop()) #2
单调栈遵循单调增或者单调减的原则,用于处理某些特别的任务,也就是常说的next greater element的问题。
class MStack:
def __init__(self):
self.stack = []
def push(self, value):
while self.stack and stack[-1] <= value:
self.pop()
self.stack.append(value)
def pop(self):
return self.stack.pop(-1)
例如,给定一个数组,求出每个数后面更大的数,这就是一个 基础next greater element的问题。比如数组是[3, 1, 2, 8, 3, 5],返回的结果为[8, 8, 8, -1, 5, -1],3的下一个最大的数为8,后面同理,8后面最大的数不存在,所以是-1。
这类题目的解法都基于如上的单调栈框架:
def nextGreatNumber(nums):
n = len(nums)
ans = [-1 for _ in range(n)]
stack = []
for i in range(n-1, -1, -1):
while stack and stack[-1] <= nums[i]:
stack.pop()
if stack:
ans[i] = stack[-1]
stack.append(nums[i])
return ans
这里有几个问题:
- 为什么要从相反的方向遍历?
要找到下一个更大的元素,可以尝试理解为从下一个更大的元素往前看,能看到nums[i]时候的元素就是下一个更大的数,因为中间的数,如果中间有一个比nums[i]更大的数,那站在后侧是看不到nums[i]的,因为被该数挡住了。
此外,留在栈中的元素都是比nums[i]大的元素,这样,栈中就是维持单调递减的一个状态,栈尾的元素永远是栈中最小的元素,也是更靠近nums[i]的元素,while循环的作用就是把比nums[i]小的元素都拿走,因为他们在前面的数看来,都会被 nums[i]挡住。 - 算法的时间复杂度是o(n^2)?
其实不然,虽然看似,有两层循环,但是实际上每个元素只会被压入栈一次, 而出栈也就仅有少数几个数(一般为两个大数,中间的小数),所以复杂度依旧是o(n)。
单调队列
队列满足先进先出原则,单调队列同理,也是专门设计出来解决某种问题,单调队列的结构如下:
class MQueue:
def __init__(self):
self.queue = []
def push(self, value):
while self.queue and self.queue[-1] < value:
self.queue.pop(-1)
self.queue.append(value)
def pop(self):
if self.queue:
return self.queue.pop(0)
在python中,单调队列在构建时单调栈类似,只是在pop时候是选择弹出列表头,其他语言多采用双向队列进行构建。
单调队列在解决某些问题时,能提升效率(如滑动窗口最大值)。