【专题讲解】单调栈

单调栈

单调栈的核心思想是维护一个单调递增或者单调递减的双向队列。队列中存储数组的索引值。

我们以一道基本例题为例,了解单调栈的简单框架。
在这里插入图片描述

这道题目是一道可以用单调栈解决的问题。我们维护一个序列,如果进来的数字比队伍右侧索引对应的数字大,弹出队列右侧。

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

这里的单调栈(递减栈)完成了三个工作:

  1. 判断是否滑窗移动之后,最大元素是否还存在
  2. 对新来的元素进行尾递补
  3. 添加新的元素

例题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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值