有趣的算法题~单调栈

在刷 LeetCode 的时候,每次遇到精彩的题解都会感叹数据结构的伟大,通过巧妙地设计,能够非常清晰明了的解决问题。

 

什么是单调栈

单调栈分为单调递增栈和单调递减栈,单调递增栈即栈内元素保持单调递增的栈,同理单调递减栈即栈内元素保持单调递减的栈,跟单调队列差不多,但是只用到它的一端,利用它可以用来解决一些 ACM/ICPC 和 OI 的题目,如 RQNOJ 的诺的队列等【来源于百度百科的定义】

 

作用

可以以 O(1) 的时间复杂度得知某个位置左右两侧比他大(或小)的数的位置,当你需要高效率获取某个位置左右两侧比他大(或小)的数的位置的时候就可以用到单调栈。

 

做题

图片

 

 

思路

可以维护一个存储下标的单调栈,从栈底到栈顶的下标对应的温度列表中的温度依次递减。如果一个下标在单调栈里,则表示尚未找到下一次温度更高的下标。正向遍历温度列表。

 

对于温度列表中的每个元素 T[i]

如果栈为空,则直接将 i 进栈,

如果栈不为空,则比较栈顶元素 prevIndex 对应的温度 T[prevIndex] 和当前温度 T[i]

如果 T[i] > T[prevIndex],则将 prevIndex 移除,并将 prevIndex对应的等待天数赋为 i - prevIndex

重复上述操作直到栈为空或者栈顶元素对应的温度小于等于当前温度,然后将 i 进栈。

 

为什么可以在弹栈的时候更新 resutl[prevIndex] 呢?

因为在这种情况下,即将进栈的 i 对应的 T[i] 一定是 T[prevIndex]右边第一个比它大的元素,试想如果 prevIndex 和 i 有比它大的元素,假设下标为 j,那么prevIndex 一定会在下标 j 的那一轮被弹掉。

由于单调栈满足从栈底到栈顶元素对应的温度递减,因此每次有元素进栈时,会将温度更低的元素全部移除,并更新出栈元素对应的等待天数,这样可以确保等待天数一定是最小的。

 

以下用一个具体的例子帮助读者理解单调栈。

对于温度列表 [73,74,75,71,69,72,76,73],单调栈 stack 的初始状态为空,结果result的初始状态是[0,0,0,0,0,0,0,0],按照以下步骤更新单调栈和答案,其中单调栈内的元素都是下标,括号内的数字表示下标在温度列表中对应的温度。

  • 当 i=0时,单调栈为空,因此将 0 进栈。

    • stack=[0(73)]

    • result=[0,0,0,0,0,0,0,0]

  • 当 i=1 时,由于 75 大于 74,因此移除栈顶元素 1,赋值 result[0]=1-0,将 1 进栈。

    • stack=[1(74)]

    • result=[1,0,0,0,0,0,0,0]

  • 当 i=2 时,由于 74 大于 73,因此移除栈顶元素 1,赋值 result[1]=2-1,将 1 进栈。

    • stack=[2(75)]

    • result=[1,1,0,0,0,0,0,0]

  • 当 i=3 时,由于 71 小于 75,因此将 3 进栈

    • stack=[2(75),3(71)]

    • result=[1,1,0,0,0,0,0,0]

  • 当 i=4 时,由于 69 小于 71,因此将 4 进栈

    • stack=[2(75),3(71),4(69)]

    • result=[1,1,0,0,0,0,0,0]

  • 当 i=5 时,由于 72 大于 69 和 71,因此依次移除栈顶元素 4 和 3,赋值 result[4]=5-4和 result[3]=5-3,将 5进栈。

    • stack=[2(75),3(72)]

    • result=[1,1,0,2,1,0,0,0]

  • 当 i=6 时,由于 76 大于 72 和 75,因此依次移除栈顶元素 5 和 2,赋值 result[5]=6-5和 result[2]=6-2,将 6进栈。

    • stack=[2(75),3(72)]

    • result=[1,1,0,2,1,0,0,0]

  • 当 i=7 时,由于 73 小于 76,因此将 7 进栈。

    • stack=[6(76),7(73)

    • result=[1,1,4,2,1,1,0,0]

 

Swift解题代码

func dailyTemperatures(_ T: [Int]) -> [Int] {
        var result = Array(repeating: 0, count: T.count)
        var stack = [Int]()
        for (index, value) in T.enumerated() {
            while !stack.isEmpty && T[stack.last!] < value {
                let preIndex = stack.popLast()!
                result[preIndex] = index - preIndex
            }
            stack.append(index)
        }

        return result
    }

欢迎关注【无量测试之道】公众号,回复【领取资源】
Python编程学习资源干货、
Python+Appium框架APP的UI自动化、
Python+Selenium框架Web的UI自动化、
Python+Unittest框架API自动化、

资源和代码 免费送啦~
文章下方有公众号二维码,可直接微信扫一扫关注即可。

备注:我的个人公众号已正式开通,致力于测试技术的分享,包含:大数据测试、功能测试,测试开发,API接口自动化、测试运维、UI自动化测试等,微信搜索公众号:“无量测试之道”,或扫描下方二维码:

 添加关注,让我们一起共同成长!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
目描述: 给定n个非负整数表示每个宽度为1的柱子的高度图,计算下雨后能够捕获的雨水量。 示例: 输入:[0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:如图所示,这是雨水的容器。 思路: 1. 定义一个栈stack,用来存储柱子的下标。定义一个变量ans,用来存储接到的雨水量。定义一个变量cur,用来存储当前的下标。 2. 遍历数组,如果stack为空或者当前柱子高度小于等于栈顶柱子高度,把当前下标入栈。 3. 如果当前柱子高度大于栈顶柱子高度,说明形成了一个凹槽,可以接到雨水。此时需要弹出栈顶元素,计算当前柱子和新的栈顶柱子之间的距离和高度差,用来计算接到的雨水量。 4. 如果当前柱子高度大于栈顶柱子高度,重复第3步,直到当前柱子高度小于等于栈顶柱子高度或者栈为空。 5. 遍历结束后,栈中剩余的柱子没有形成凹槽,无法接到雨水,将栈中剩余的柱子依次出栈,计算接到的雨水量。 代码实现: C++实现: class Solution { public: int trap(vector<int>& height) { stack<int> s; int ans = 0; for (int i = 0; i < height.size(); i++) { while (!s.empty() && height[i] > height[s.top()]) { int cur = s.top(); s.pop(); if (s.empty()) break; int left = s.top(); int right = i; int h = min(height[left], height[right]) - height[cur]; ans += h * (right - left - 1); } s.push(i); } return ans; } }; Python实现: class Solution: def trap(self, height: List[int]) -> int: s = [] ans = 0 for i in range(len(height)): while s and height[i] > height[s[-1]]: cur = s[-1] s.pop() if not s: break left = s[-1] right = i h = min(height[left], height[right]) - height[cur] ans += h * (right - left - 1) s.append(i) return ans

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wu_Candy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值