962. Maximum Width Ramp
题目里的 ramp 很直接:找一对下标 (i, j),满足 i < j 且 nums[i] <= nums[j],宽度就是 j - i。要求的是全数组里的最大宽度;如果根本找不到这样的组合,返回 0。
这个题的关键不在'怎么找',而在'别把时间花在没必要的比较上'。暴力枚举当然能写,但数据范围到 5 * 10^4,双重循环基本不用考虑。更稳的办法是先把左边候选点筛出来,再从右往左去试着把宽度拉大。
思路
先维护一个单调递减的下标栈。栈里存的不是所有位置,而是从左到右扫描时,nums[i] 形成新低点的那些下标。这样做的原因很简单:
- 左侧值越小,越容易和后面的元素组成合法 ramp;
- 不是新低点的位置,通常没必要再保留,它们不会比更左边、更小的值更有优势。
接着从右往左扫 j。只要当前栈顶下标对应的值 nums[stack[top]] <= nums[j],这对下标就能组成一个 ramp,宽度是 j - stack[top]。然后把这个栈顶弹掉,继续尝试更靠左的候选点。
这里的弹栈是有意义的。对同一个左端点来说,当前的 j 已经是从右往左扫描到的最靠右位置了,后面再遇到的 j 只会更小,宽度不会更大。留着它没有收益。
代码
int maxWidthRamp(int* nums, int numsSize) {
if (numsSize < 2) return 0;
// 单调递减栈,存下标
int* stack = (int*)malloc(numsSize * sizeof(int));
int top = -1;
// 从左到右,记录新的最小值位置
for (int i = 0; i < numsSize; ++i) {
if (top == -1 || nums[i] < nums[stack[top]]) {
stack[++top] = i;
}
}
int maxWidth = 0;
// 从右往左尝试扩展宽度
for (int j = numsSize - ; j >= && top >= ; --j) {
(top >= && nums[[top]] <= nums[j]) {
width = j - [top];
(width > maxWidth) maxWidth = width;
--top;
}
}
();
maxWidth;
}


