leetcode单调栈

目录

  1. 柱状图中最大的矩形
  2. 最小栈
  3. 下一个更大元素 I
  4. 下一个更大元素 II
  5. 每日温度
  6. 子数组的最小值之和
  7. 移掉 K 位数字

定义:单调栈是一种特殊的栈,在这个栈内只储存递增或递减的数组。
例题:

84.柱状图中最大的矩形

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

在这里插入图片描述
示例:

输入: [2,1,5,6,2,3]
输出: 10

解题思路:(利用单调栈)

首先在数组最后加入0,这是为了方便处理完所有高度数据,假设储存高度坐标的栈为stack,当前处理的高度坐标为i:

  1. 如果当前stack为空,或者height[i]>=栈顶对应坐标的高度,则进站,i+=1
  2. 当height[i] <=栈顶元素,说明可以处理栈内的坐标形成的局部递增高度。求解当前栈内形成的最大面积值。top=弹出栈顶坐标,top’ =此时栈顶新坐标。对应计算面积宽度k = i-1-top’。(若出站后stack为空,则对应宽度k=i),面积s=heights[top] *k。再次返回2检查比较。
  3. 遍历完所有i,返回面积最大值。
    代码如下:
def largestRectangleArea(heights):
	i=0
	max_area=0
	stack=[]
	heights.append(0)
	while i< len(heights):
		if len(stack)==0 or heights[stack[-1]]<=heights[i]:
			stack.append(i)
			i+=1
		else:
			top=stack.pop()
			if len(stack)==0:
				max_area=max(max_area,i*heights[top])
			else:
				max_area=max(max_area,(i-stack[-1]-1)*heihts[top])
	return max_area	

155. 最小栈
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

辅助栈:在每个元素 a 入栈时把当前栈的最小值 m 存储起来。在这之后无论何时,如果栈顶元素是 a,我们就可以直接返回存储的最小值 m。

class MinStack:
    def __init__(self):
        self.stack = []
        self.min_stack = [math.inf]

    def push(self, x: int) -> None:
        self.stack.append(x)
        self.min_stack.append(min(x, self.min_stack[-1]))

    def pop(self) -> None:
        self.stack.pop()
        self.min_stack.pop()

    def top(self) -> int:
        return self.stack[-1]

    def getMin(self) -> int:
        return self.min_stack[-1]

496. 下一个更大元素 I

给定两个没有重复元素的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。找到 nums1 中每个元素在 nums2 中的下一个比其大的值。

nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出-1。

在这里插入图片描述

解题思路:这道题本质上就是对第二个数组进行操作,找出其中所有元素对应的数字,(第一个比它大的元素或者-1),这一步用字典储存,用栈结构实现。栈内元素储存的是递减组数。

class Solution(object):
    def nextGreaterElement(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: List[int]
        """
        dict={}
        stack=[]
        res=[]
        for i in nums2:
            while stack and stack[-1]<i:
                dict[stack[-1]]=i
                stack.pop()
            stack.append(i)  #栈内储存的是单调递减数组
        for i in nums1:
            res.append(dict.get(i,-1))
        return ls_ans 

503. 下一个更大元素 II

给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。

示例1:

输入: [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数; 
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

解题思路:

通过单调栈来解决,但此时输入的是一个数组而且包含重复元素。对于循环数组,通常的处理手段是通过余数,然后将数组的长度扩大两倍即可。关键问题,在于如果找不到下一个更大的元素?可不需要弹出栈顶元素。此时的栈是一非严格单调的栈。

class Solution:
    def nextGreaterElements(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        stack, nums_len = list(), len(nums)
        res = [-1] * nums_len
        for i in range(nums_len*2):
            while stack and nums[stack[-1]] < nums[i%nums_len]:
                res[stack.pop()] = nums[i%nums_len]   
            stack.append(i%nums_len)
            
        return res

739. 每日温度

根据每日 气温 列表,请重新生成一个列表,对应位置的输入是你需要再等待多久温度才会升高的天数。如果之后都不会升高,请输入 0 来代替。

例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]

代码如下:

class Solution(object):
    def dailyTemperatures(self, T):
        """
        :type T: List[int]
        :rtype: List[int]
        """
        res=[0]*len(T)
        stack=[]
        for i in range(len(T)):
            while stack and T[stack[-1]]<T[i]:
                index=stack.pop()
                res[index]=i-index
            stack.append(i)   
        return res

907. 子数组的最小值之和

给定一个整数数组 A,找到 min(B) 的总和,其中 B 的范围为 A 的每个(连续)子数组。

由于答案可能很大,因此返回答案模 10^9 + 7。

示例:

输入:[3,1,2,4]
输出:17
解释:
子数组为 [3],[1],[2],[4],[3,1],[1,2],[2,4],[3,1,2],[1,2,4],[3,1,2,4]。 
最小值为 3,1,2,4,1,1,2,1,1,1,和为 17。

解题思路:

使用递增栈:当我们找到当前元素下一个更小值的同时,实际上也可以知道前一个更小的元素。

代码如下:

class Solution(object):
    def sumSubarrayMins(self, A):
        """
        :type A: List[int]
        :rtype: int
        """
        res = 0
        stack = []
        A = [0] + A + [0]
        for i in range(len(A)):
            while stack and A[stack[-1]] > A[i]:
                now_index= stack.pop()
                res += A[now_index] * (i - now_index) * (now_index - stack[-1])
            stack.append(i)
        return res % (10**9 + 7)

402. 移掉 K 位数字
贪心 + 单调栈

class Solution:
    def removeKdigits(self, num: str, k: int) -> str:
        numStack = []
        
        # 构建单调递增的数字串
        for digit in num:
            while k and numStack and numStack[-1] > digit:
                numStack.pop()
                k -= 1
        
            numStack.append(digit)
        
        # 如果 K > 0,删除末尾的 K 个字符
        finalStack = numStack[:-k] if k else numStack
        
        # 抹去前导零
        return "".join(finalStack).lstrip('0') or "0"
### LeetCode 单调栈技巧解析 单调栈是一种特殊的栈数据结构,在处理特定类型的算法问题时非常有效。这类问题通常涉及寻找某个元素左边或右边第一个大于或小于该元素的位置。 #### 1. 基本概念 单调栈分为两种形式:**单调递增栈** 和 **单调递减栈**。前者用于解决「下一个更大元素」类的问题;后者适用于「下一个更小元素」的情况[^4]。 对于给定序列中的每一个位置,如果想要找到其右侧最近的一个比自己大的数,则可以采用单调递增栈来完成这一目标。每当遇到一个新的数值时,会持续移除栈顶那些不大于新加入值的成员直到满足条件为止,并记录下这些被移除项对应的结果。之后再把新的值压入栈底继续上述过程直至遍历结束[^1]。 ```python def nextGreaterElement(nums1, nums2): """ :type nums1: List[int] :type nums2: List[int] :rtype: List[int] """ dict_ = {} stack = [] for num in nums2: while stack and stack[-1] < num: dict_[stack.pop()] = num stack.append(num) result = [dict_.get(x, -1) for x in nums1] return result ``` 此代码片段展示了如何利用单调递增栈求解`nums2`中各元素在其后的首个较大者,并通过字典保存映射关系以便快速查询来自`nums1`列表内的请求。 #### 2. 应用场景举例 - **股票最大利润计算** 当面对像买卖股票获取最大收益这样的实际应用案例时,也可以借助类似的思路——即维护一个最小买入价格变量并不断更新潜在的最大差额作为最终获利金额[^2]: ```c int maxProfit(int* prices, int pricesSize) { if (pricesSize <= 1) { return 0; } int maxprof = 0; int minprice = prices[0]; for (int i = 0; i < pricesSize; ++i) { if (prices[i] > minprice) { maxprof = fmax(maxprof, prices[i] - minprice); } else { minprice = prices[i]; } } return maxprof; } ``` - **去除重复字母** 另外还有诸如删除字符串里的冗余字符以形成字典序最小的新串等问题也能运用到相同的技术框架之上。这里的关键在于维持一个不含任何前导相同字符且保持严格升序排列的状态机(即所谓的“单调栈”),从而确保每次新增加进来的小写字母都能恰当地安置在合适之处而不破坏整体顺序性[^3]: ```python from collections import Counter class Solution: def removeDuplicateLetters(self, s: str) -> str: stack = [] remains = Counter(s) for char in s: if char not in stack: while stack and remains[stack[-1]] > 0 and stack[-1] > char: stack.pop() stack.append(char) remains[char] -= 1 return ''.join(stack) ``` 综上所述,掌握好单调栈这种高效的数据结构及其背后的工作原理能够帮助更好地理解和应对一系列经典的编程挑战题目。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值