第一式【单调栈】

一.什么是单调栈?

  • 栈内元素单调的栈,分为单调递增或者单调递减两种。

二.什么时候用这种特殊的结构?

  • 在需要通过比较数组或者队列的前后元素来解决问题时。

三.介绍一下两种栈

1.单调递增栈

    - 针对列表中每一个元素从它右边开始查找第一个比它小的元素。
    - 针对列表中每一个元素从它左边开始查找第一个比它小的元素。(从后往前遍历)

2.单调递减栈

  - 针对列表中每一个元素从它右边开始查找第一个比它大的元素。
  - 针对列表中每一个元素从它左边开始查找第一个比它小的元素。(从后往前开始遍历)

四.实战招数

  • 单调栈该怎么用?
    从题目抽象出来的具体问题一般可以分为两步:
    1.怎么快速的找到元素两侧第一个符合要求的元素
    2.怎么把找到的结果储存起来

  • 实际会遇到的情况?
    待处理的列表一般有两种形式,一种是元素不重复,一种是元素重复,或元素需要查询的结果和初始列表位置相关。

    1. 元素不重复的可以用字典来存储查询结果,查找速度快。
    2. 元素重复的,或者答案依赖位置的,要用列表来存储结果,有一些找不到位置的,可以在初始化的时候就进行填充。
  • 流程图

屏幕快照 2022-01-03 下午8.07.19.png

五.练兵

"""496. 下一个更大元素 I
难度
简单
619
nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。
给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。
对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。
返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。
示例 1:
输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
- 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
- 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。
- 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
示例 2:
输入:nums1 = [2,4], nums2 = [1,2,3,4].
输出:[3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
- 2 ,用加粗斜体标识,nums2 = [1,2,3,4]。下一个更大元素是 3 。
- 4 ,用加粗斜体标识,nums2 = [1,2,3,4]。不存在下一个更大元素,所以答案是 -1 。
提示:
1 <= nums1.length <= nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 104
nums1和nums2中所有整数 互不相同
nums1 中的所有整数同样出现在 nums2 中
进阶:你可以设计一个时间复杂度为 O(nums1.length + nums2.length) 的解决方案吗?"""

-题解-

class Solution:

    def nextGreaterElement(self, nums1, nums2):
        """
        设计好套路准备函数
        :param nums1: 
        :param nums2: 
        :return: 
        """
        # 传入
        res = self.monotonic_stack(nums2)
        # 拼凑答案
        ans = []
        for each in nums1:
            if res.get(each):
                ans.append(res.get(each))
            else:
                ans.append(-1)
        return ans

    def monotonic_stack(self,nums2):
        """
        专门处理查询+储存
        :param nums2: 
        :return: 
        """
        n = len(nums2)
        stack = []
        res = {}
        for i in range(n):
            while stack and stack[-1] < nums2[i]:
                tmp = stack.pop()
                res[tmp] = nums2[i]
            stack.append(nums2[i])
        return res


nums1 = [4,1,2]
nums2 = [1,3,4,2]
a = Solution().nextGreaterElement(nums1,nums2)
print(a)

这个题目的特点:没有重复元素
所以可以,用字典来存储

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

示例 1:
输入: [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数; 
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。
注意: 输入数组的长度不会超过 10000。"""```

 

-题解-

class Solution:
    def nextGreaterElements(self, nums):
        """
        :param nums:
        :return:
        """
        lenth = len(nums)
        # 循环数组的朴素处理方法,拼接
        for i in range(lenth):
            nums.append(nums[i])
        # 输入
        res = self.monotonic_stack(nums,lenth)
        return res[0:lenth]

    def monotonic_stack(self,nums,lenth):
        """
        专门处理加储存
        :param nums:
        :param lenth:
        :return:
        """
        lenth = lenth *2
        # 因为是有序的且有重复的,所以用列表来存储答案
        res = [-1]*lenth
        stack = []
        for i in range(lenth):
            while stack and nums[stack[-1]] <nums[i]:
                ind = stack.pop()
                # 存位置
                res[ind] = nums[i]
            stack.append(i)
        return res
nums = [1, 2, 1]
print(s.nextGreaterElements(nums))
"""739. 每日温度  
请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。
示例 1:
输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]
示例 2:
输入: temperatures = [30,40,50,60]
输出: [1,1,1,0]
示例 3:
输入: temperatures = [30,60,90]
输出: [1,1,0]
提示:
1 <= temperatures.length <= 105
30 <= temperatures[i] <= 100"""```

-题解-

class Solution:
    def dailyTemperatures(self,temperatures):
        res = self.monotonic_stack(temperatures)
        n = len(temperatures)
        for i in range(n):
            if res[i]!=0:
                res[i] = res[i]-i
        return res

    def monotonic_stack(self, temperatures):
        lenth = len(temperatures)
        stack =[]
        res = [0]*lenth
        for i in range(lenth):
            while stack and temperatures[stack[-1]] < temperatures[i]:
                index = stack.pop()
                res[index] = i
            stack.append(i)
        return res
"""901. 股票价格跨度
编写一个 StockSpanner 类,它收集某些股票的每日报价,并返回该股票当日价格的跨度。
今天股票价格的跨度被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。
例如,如果未来7天股票的价格是 [100, 80, 60, 70, 60, 75, 85],那么股票跨度将是 [1, 1, 1, 2, 1, 4, 6]。
示例:
输入:["StockSpanner","next","next","next","next","next","next","next"], [[],[100],[80],[60],[70],[60],[75],[85]]
输出:[null,1,1,1,2,1,4,6]
解释:
首先,初始化 S = StockSpanner(),然后:
S.next(100) 被调用并返回 1,
S.next(80) 被调用并返回 1,
S.next(60) 被调用并返回 1,
S.next(70) 被调用并返回 2,
S.next(60) 被调用并返回 1,
S.next(75) 被调用并返回 4,
S.next(85) 被调用并返回 6。
注意 (例如) S.next(75) 返回 4,因为截至今天的最后 4 个价格
(包括今天的价格 75) 小于或等于今天的价格。
提示:
调用 StockSpanner.next(int price) 时,将有 1 <= price <= 10^5。
每个测试用例最多可以调用  10000 次 StockSpanner.next。
在所有测试用例中,最多调用 150000 次 StockSpanner.next。
此问题的总时间限制减少了 50%。"""
class StockSpanner:

    def __init__(self):
        self.stack = []
        self.prices = []

    def next(self, price):
        """

        :param price:
        :return:
        """
        # 处理输入
        self.prices.append(price)
        # 传入输入
        res = self.monotonic_stack(price)
        return res

    def monotonic_stack(self, price):
        """

        :param price:
        :return:
        """
        # 价格是每次才传进来的
        n = len(self.prices)-1
        # 天数跨度默认为1,跨度要每次计算,跨度要和价格一一对应,这些都要存下来
        day = 1
        while self.stack and price >= self.prices[self.stack[-1][0]]:
            inx = self.stack.pop()
            # 弹出的时候,加上被弹出项的跨度
            day += inx[1]
        self.stack.append((n,day))
        return day


s = StockSpanner()
nums = [100, 80, 60, 70, 60, 75, 85]
ans = []
for each in nums:
    res = s.next(each)
    # 处理输出
    ans.append(res)
print(ans)```



©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容