题目描述
思路解析动画文字版
记住「左建递减候选栈,右扫能配就弹栈取最宽」,下面每帧都在套它。
第 0 个 9 直接入栈,作为第一个候选左端点(蓝色)。
8 比栈顶值 9 还小,刷新出值更小的新候选左端点(它下标更靠右、但值更小),入栈(蓝色)。
1 比栈顶值 8 还小,刷新出值更小的新候选左端点(它下标更靠右、但值更小),入栈(蓝色)。
0 比栈顶值 1 还小,刷新出值更小的新候选左端点(它下标更靠右、但值更小),入栈(蓝色)。
1 ≥ 栈顶值 0:它当左端点不如栈顶(栈顶更靠左、值还不比它大),跳过不入栈。
9 ≥ 栈顶值 0:它当左端点不如栈顶(栈顶更靠左、值还不比它大),跳过不入栈。
4 ≥ 栈顶值 0:它当左端点不如栈顶(栈顶更靠左、值还不比它大),跳过不入栈。
0 ≥ 栈顶值 0:它当左端点不如栈顶(栈顶更靠左、值还不比它大),跳过不入栈。
4 ≥ 栈顶值 0:它当左端点不如栈顶(栈顶更靠左、值还不比它大),跳过不入栈。
1 ≥ 栈顶值 0:它当左端点不如栈顶(栈顶更靠左、值还不比它大),跳过不入栈。
第一步完成。蓝色这些下标是全部「候选左端点」,它们的值严格递减。接下来 j 从最右端往左走,逐个尝试配坡。
j 来到下标 9(值 1)。从栈顶开始看:栈顶下标的值若 ≤ 1 就能配成坡。
栈顶下标 3(值 0) ≤ 1,配成一个坡,宽度 9−3=6(绿色)。比之前更宽,刷新最大宽度为 6。弹出栈顶(它再往左的 j 只会更窄),继续看新栈顶。
栈顶下标 2(值 1) ≤ 1,配成一个坡,宽度 9−2=7(绿色)。比之前更宽,刷新最大宽度为 7。弹出栈顶(它再往左的 j 只会更窄),继续看新栈顶。
现在栈顶值 8 比 1 大,配不成坡(需要左端点 ≤ 右端点)。j 继续往左走。
j 来到下标 8(值 4)。从栈顶开始看:栈顶下标的值若 ≤ 4 就能配成坡。
现在栈顶值 8 比 4 大,配不成坡(需要左端点 ≤ 右端点)。j 继续往左走。
j 来到下标 7(值 0)。从栈顶开始看:栈顶下标的值若 ≤ 0 就能配成坡。
现在栈顶值 8 比 0 大,配不成坡(需要左端点 ≤ 右端点)。j 继续往左走。
j 来到下标 6(值 4)。从栈顶开始看:栈顶下标的值若 ≤ 4 就能配成坡。
现在栈顶值 8 比 4 大,配不成坡(需要左端点 ≤ 右端点)。j 继续往左走。
j 来到下标 5(值 9)。从栈顶开始看:栈顶下标的值若 ≤ 9 就能配成坡。
栈顶下标 1(值 8) ≤ 9,配成一个坡,宽度 5−1=4(绿色)。没超过已知最大 7。弹出栈顶(它再往左的 j 只会更窄),继续看新栈顶。
栈顶下标 0(值 9) ≤ 9,配成一个坡,宽度 5−0=5(绿色)。没超过已知最大 7。弹出栈顶(它再往左的 j 只会更窄),继续看新栈顶。
候选栈已经空了,说明每个候选左端点都找到了它能配的最靠右的 j。可以收工,最大宽度 7。
最宽的坡是绿色这一段:左端 i=2(值 1)、右端 j=9(值 1),满足 1 ≤ 1,宽度 7。这就是答案。
边界先想清:全递减为 0、全递增取首尾、单元素为 0。
面试追问二分替代解法与「栈为何严格递减」的正确性。
参考代码
from typing import Listclass Solution: def maxWidthRamp(self, nums: List[int]) -> int: stack = [] for i, x in enumerate(nums): if not stack or x < nums[stack[-1]]: stack.append(i) ans = 0 for j in range(len(nums) - 1, -1, -1): while stack and nums[stack[-1]] <= nums[j]: ans = max(ans, j - stack.pop()) return ans复杂度
- 时间:O(n),建栈一趟 + 右扫一趟,每个下标至多入栈/出栈一次
- 空间:O(n),候选下标栈最多 n 个
易错点
面试追问把动画讲成自己的话
追问为什么不用对每个 j 二分找最左的可行 i?
追问候选栈里的值为什么一定严格递减?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
反转每对括号间的子串
LeetCode 1190 · 中等 · 沿着 栈套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题