【九阴真经】第一式:单调栈

141 阅读3分钟

84. 柱状图中最大的矩形

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

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

示例1

输入: heights = [2,1,5,6,2,3]
输出: 10
解释: 最大的矩形为图中红色区域,面积为 10

解题流程1

func largestRectangleArea(heights []int) int {
    statck:=[]int{}
    sum:=0
    for i:=0;i<len(heights);i++{
        for len(statck)>0 && heights[i]<heights[statck[len(statck)-1]]{
            w:=i-statck[len(statck)-1]
            h:=heights[statck[len(statck)-1]]
            statck=statck[:len(statck)-1]
            sum=max(sum,w*h)
        }
        statck=append(statck,i)
    }
    return sum
}

func max(a,b int)int{
    if a>b{
        return a
    }
    return b
}

解题分析1

按照上面的方法,发现部分通过。
根据测试用例,可以想到有一种情况:如果数据本身就是递增排序的。那么栈里面的元栈顶到栈底是:4,2
这样始终无法进入上述题解中的for循环

image.png

解题流程2

func largestRectangleArea(heights []int) int {
    stack:=[]int{}
    temp:=[]int{}
    sum:=0
    temp=append(temp,0)
    temp=append(temp,heights...)
    temp=append(temp,0)
    for i:=0;i<len(temp);i++{
        for len(stack)>0 && temp[i]<temp[stack[len(stack)-1]]{
            h:=temp[stack[len(stack)-1]]
            stack=stack[:len(stack)-1]
            w:=i-stack[len(stack)-1]-1
            sum=max(sum,w*h)
        }
        stack=append(stack,i)
    }
    return sum
}

func max(a,b int)int{
    if a>b{
        return a
    }
    return b
}

解题分析2

数组前后添加了哨兵,解决上述存在的问题

739. 每日温度

给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。

示例1

输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]

解题流程1

func dailyTemperatures(temperatures []int) []int {
    stack:=[]int{}
    res:=make([]int,len(temperatures))
    for i:=0;i<len(temperatures);i++{
        value:=0
        if len(stack)>0 && temperatures[i]>temperatures[stack[len(stack)-1]]{
            value=i-stack[len(stack)-1]
            res[stack[len(stack)-1]]=value
            stack=stack[:len(stack)-1]
        }
        stack=append(stack,i)
    }
    return res
}

解题分析1

首先本题首先想到的是暴力,双层for循环,第一层循环定位当前温度,第二层循环定位在当前温度数组中寻找大于当前温度的温度。
上述方法是时间复杂度较高。
由于当前元素始终是找大于当前温度的温度,考虑到空间换时间,想到了单调栈,使用栈存储温度的下标。
因此,写出上面的方法,部分通过。
问题点在于,当遇到一个较高温度的时候,只考虑比前一个温度值高,而没有考虑她可能比早之前的某些温度也高。因此,导致早之前的温度值没被刷新。

image.png

解题流程2

func dailyTemperatures(temperatures []int) []int {
    stack:=[]int{}
    res:=make([]int,len(temperatures))
    for i:=0;i<len(temperatures);i++{
        value:=0
        for len(stack)>0 && temperatures[i]>temperatures[stack[len(stack)-1]]{
            value=i-stack[len(stack)-1]
            res[stack[len(stack)-1]]=value
            stack=stack[:len(stack)-1]
        }
        stack=append(stack,i)
    }
    return res
}

解题分析2

通过对未通过的测试用例分析,将内层循环中的if 改为for,实现对早之前温度值的刷新。

503. 下一个更大元素 II

给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。

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

示例1

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

解题流程

func nextGreaterElements(nums []int) []int {
    stack:=[]int{}
    nums=append(nums,nums...)
    res:=make([]int,len(nums))
    for i:=0;i<len(nums);i++{
        res[i]=-1
    }
    for i:=0;i<len(nums);i++{
        for len(stack)>0 && nums[i]>nums[stack[len(stack)-1]]{
            res[stack[len(stack)-1]]=nums[i]
            stack=stack[:len(stack)-1]
        }
        stack=append(stack,i)
    }
    return res[:len(nums)/2]
}