刷题论 02|单调栈并没有你想的那么难

289 阅读3分钟

本文为视频的原稿,访问 B 站看视频,通过视频更能领会要义哦!

使用场景 & 原理

解决的问题:在数组中,寻找下一个更大(小)的元素。

原理:

  • 空间换时间,暴力解法,O(n^2) 可解,借助额外空间实现了降维,O(n) 可解。
  • 求下一个更大元素时,构造单调递减的栈,遍历时一旦找到比栈顶大的元素,就可以把栈顶弹出,视为找到了栈顶元素的下一个更大元素。

示例

这一段视频里有详细解释单调栈的构建过程。

// 遍历下标:                i
// arr:    73,74,75,71,69,72,76,73
// stack: 75,71,69,
// res: 74,75,76,72,72,76,?,?

代码模板

stack := make([]int, 0)
for i, v := range arr {
    // 当前元素比栈顶大,视为找到下一个更大元素,就弹出栈顶,更新结果
    for len(stack) > 0 && v > arr[stack[len(stack)-1]] {
            topIndex := stack[len(stack)-1]
            stack = stack[:len(stack)-1]
            // 更新结果
    }
    // 当前元素比栈顶小,直接把下标入栈
    stack = append(stack, i)
}

LeetCode 实战

以下实战题在我的主页都有讲过,欢迎观看:访问 B 站

739 - 每日温度

func dailyTemperatures(temperatures []int) []int {
    ans := make([]int, len(temperatures))
    stack := make([]int, 0)
    for i, t := range temperatures {
        // 当前元素比栈顶要大,更新结果,再去弹出栈顶元素
        for len(stack) > 0 && t > temperatures[stack[len(stack)-1]] {
            topIndex := stack[len(stack)-1]
            ans[topIndex] = i - topIndex
            stack = stack[:len(stack) - 1]
        }
        // 当前元素比栈顶要小,直接入栈
        stack = append(stack, i)
    }
    return ans
}

// 遍历过程中,找到右侧第一个比自己大的元素的下标
// 暴力解法:O(N^2)
// 降维:空间换时间
// 数组,从左往右遍历,记录第一个比自己大的元素,找到后更新result,就可以把自己从数组中删去
// 栈来解决,单调栈

496 - 下一个更大元素 I

func nextGreaterElement(nums1 []int, nums2 []int) []int {
    hashmap := map[int]int{}
    res := make([]int, len(nums1))
    stack := make([]int, 0)
    for i, v := range nums2 {
            // 找到比自己大的元素,就可以弹出
            for len(stack) > 0 && v > nums2[stack[len(stack)-1]] {
                    hashmap[nums2[stack[len(stack)-1]]] = v
                    stack = stack[:len(stack)-1]
            }
            // 没有比自己大的,就放到栈中。把什么元素存到栈中,是看最终结果需要什么
            stack = append(stack, i)
    }
    for i := range nums1 {
            if val, ok := hashmap[nums1[i]]; ok {
                    res[i] = val
            } else {
                    res[i] = -1
            }
    }
    return res
}

503 - 下一个更大元素 II

func nextGreaterElements(nums []int) []int {
    res := make([]int, len(nums))
    for i := range res {
            res[i] = -1
    }
    stack := make([]int, 0)
    for i := 0; i < len(nums)*2; i++ {
            index := i % len(nums)
            // 找到比自己大的元素,就可以弹出
            for len(stack) > 0 && nums[index] > nums[stack[len(stack)-1]] {
                    res[stack[len(stack)-1]] = nums[index]
                    stack = stack[:len(stack)-1]
            }
            // 没有比自己大的,就放到栈中。把什么元素存到栈中,是看最终结果需要什么
            stack = append(stack, index)
    }
    return res
}

84 - 柱状图中最大的矩形

func largestRectangleArea(heights []int) int {
    newHeights := make([]int, len(heights)+2)
    for i := 0; i < len(heights); i++ {
            newHeights[i+1] = heights[i]
    }
    res := 0
    stack := make([]int, 0)
    for i, height := range newHeights {
        for len(stack) > 0 && height < newHeights[stack[len(stack)-1]] {
            mid := stack[len(stack)-1]
            stack = stack[:len(stack)-1]
            if len(stack) > 0 {
                    h := newHeights[mid]
                    w := i - stack[len(stack)-1] - 1
                    res = max(res, h*w)
            }
        }
        stack = append(stack, i)
    }
    return res
}

func max(x, y int) int {
    if x > y {
            return x
    }
    return y
}

// 找到比自身柱子低的左右柱子,面积=自身高度*左右柱子的距离
// 用单调递增的栈,左右柱子就方便找到

42 - 接雨水

func trap(height []int) int {
    sum := 0
    stack := make([]int, 0)
    for i, h := range height {
        //	遇到比栈顶元素高的,就弹出
        for len(stack) > 0 && h > stack[len(stack)-1] {
                mid := stack[len(stack)-1]
                stack = stack[:len(stack)-1]
                if len(stack) > 0 {
                        h := min(height[stack[len(stack)-1]], h) - height[mid]
                        w := i - stack[len(stack)-1] - 1
                        sum += h * w
                }
        }
        //	比自己小,直接入栈
        stack = append(stack, i)
    }
    return sum
}

func min(x, y int) int {
    if x > y {
        return y
    }
    return x
}

// 相对于自身,找左右比自己高的柱子
// 面积 = 矮柱子的高度 * 左右柱子的距离

LeetCode 练习