单调栈
单调栈的核心思想是维护一个单调递增或者单调递减的双向队列。队列中存储数组的索引值。
我们以一道基本例题为例,了解单调栈的简单框架。
这道题目是一道可以用单调栈解决的问题。我们维护一个序列,如果进来的数字比队伍右侧索引对应的数字大,弹出队列右侧。
class Solution(object):
def dailyTemperatures(self, T):
# 单调栈
ans = [0]*len(T)
cur = [[T[0], 0]]
## 升序排列
for i in range(1,len(T)):
while cur and T[i] > cur[-1][0]:
value, index = cur.pop()
ans[index] = i-index
cur.append([T[i], i])
return ans
单调栈其实应用的场合并不多,除了这类寻找下一个更大的数字之外的题目,还有一类题目是寻找固定长度的区间的最值。
例题1:滑动窗最大值
from collections import deque
class Solution:
def maxSlidingWindow(self, nums, k):
queue = deque()
n = len(nums)
ans = []
def stack(i):
if queue and queue[0] == i-k:
queue.popleft()
while queue and nums[queue[-1]]<=nums[i]:
queue.pop()
queue.append(i)
for i in range(n):
# 进入单调栈,进行判断
stack(i)
if i>=k-1:
ans.append(nums[queue[0]])
return ans
这里的单调栈(递减栈)完成了三个工作:
- 判断是否滑窗移动之后,最大元素是否还存在
- 对新来的元素进行尾递补
- 添加新的元素
例题2 带限制的子序列和
这道题可以选择dp的方法
dp[i]
:选取nums[i]
时的最优情况- 转移方程:
dp[i]=max(dp[i-j]+nums[i], nums[i]) for j in range(1,k)
因此,可以看出来,其实这个问题可以转换为,每次在一个长度为k
的窗口中选取dp[i]
的最大值。
class Solution:
def constrainedSubsetSum(self, nums: List[int], k: int) -> int:
n = len(nums)
queue = collections.deque()
dp = nums[:]
ans = nums[0]
# 单调栈
def stack(i):
if queue and queue[0] == i-k:
queue.popleft()
# 注意每次在dp上进行滑动
while queue and dp[queue[-1]]<=dp[i]:
queue.pop()
queue.append(i)
for i in range(1,n):
stack(i-1)
dp[i] = max(dp[queue[0]]+nums[i],nums[i])
ans = max(ans,dp[i])
return ans