AlgoMaster Logo

Maximum Width Ramp

mediumFrequencyUpdated September 21, 2026

Understanding the Problem

We need to find two indices i and j where i < j and nums[i] <= nums[j], and we want to maximize the gap j - i. In other words, we're looking for the widest possible "ramp" where the value at the left end doesn't exceed the value at the right end.

A brute force approach checks every pair (i, j), which is O(n^2). To do better, we need to avoid examining every pair.

The left endpoint i of an optimal ramp is always an index whose value is a "new minimum" as we scan left to right. If nums[i] is not smaller than everything before it, then some earlier index has a value less than or equal to nums[i], and that earlier index pairs with the same j to form a wider ramp. So the candidates for i form a decreasing sequence of values, which a monotonic stack captures directly.

Key Constraints:

  • 2 <= nums.length <= 5 * 10^4 → With n up to 50,000, an O(n^2) solution performs around 1.25 billion comparisons in the worst case, which is too slow. We need O(n log n) or O(n).
  • 0 <= nums[i] <= 5 * 10^4 → Values are non-negative and fit comfortably in a 32-bit integer, so widths and comparisons never overflow.

Approach 1: Brute Force

Intuition

For each left endpoint i, the widest ramp starting there ends at the farthest j with nums[j] >= nums[i]. So instead of testing every j, scan from the right end inward and stop at the first j that works — it is already the farthest one.

The scan still touches every pair in the worst case, so it runs in O(n^2) time, but it exits early on most inputs.

Algorithm

  1. Initialize maxWidth = 0.
  2. For each index i from 0 to n - 1:
    • Scan j from n - 1 down to i + 1.
    • At the first j where nums[j] >= nums[i], update maxWidth = max(maxWidth, j - i) and stop scanning: no smaller j can give a wider ramp starting at i.
  3. Return maxWidth.

Visualization and Code

Loading animation...

Most of these pairs are irrelevant. If nums[i] is not a "new minimum" going left to right, an earlier index with a smaller value gives a wider ramp with the same right endpoint. The next approach removes the quadratic work by sorting on value and reducing the problem to a gap between original indices.

Approach 2: Sort with Index Tracking

Intuition

Sort the elements by value while keeping their original indices. For any two elements in sorted order, the one that appears earlier has a value less than or equal to the later one, so the ramp condition nums[i] <= nums[j] holds for every earlier-later pair. The problem reduces to finding the maximum difference between original indices, where the smaller index comes first in the array.

Iterating through the sorted order, we track the minimum original index seen so far, starting it at n so the first element always claims it. For each element, if its original index is greater than that minimum, the pair forms a valid ramp and the candidate width is the difference; otherwise the element itself becomes the new minimum. The maximum across all elements is the answer. Tie-breaking by index keeps equal values in increasing index order, so equal-valued elements still form valid ramps with each other.

Algorithm

  1. Create an array of indices [0, 1, 2, ..., n-1] and sort them by the values in nums. Use index as a tiebreaker (smaller index first).
  2. Initialize maxWidth = 0 and minIndex = n, a sentinel larger than any real index.
  3. For each index idx in the sorted order:
    • If idx > minIndex, the pair forms a valid ramp: update maxWidth = max(maxWidth, idx - minIndex).
    • Otherwise idx is the smallest original index seen so far: set minIndex = idx.
  4. Return maxWidth.

Visualization and Code

Loading animation...

The sorting step dominates the runtime at O(n log n). The next approach identifies the useful left endpoints in one linear pass and matches them with right endpoints in another, dropping the sort and reaching O(n).

Approach 3: Monotonic Stack (Optimal)

Intuition

This solution runs in O(n) time and rests on two observations.

First, the left endpoint of the optimal ramp belongs to the strictly decreasing prefix of values. If nums[i] is not a new minimum, then some i' < i has nums[i'] <= nums[i], and for any valid ramp (i, j) the pair (i', j) is also valid and at least as wide. So the only left-endpoint candidates are indices that form a strictly decreasing sequence from the start, which we collect in a stack during a left-to-right pass.

Second, for the right endpoint we want the largest possible j, so we scan from right to left. For each j, while nums[j] is at least the value at the top of the stack, that stack index is a valid left endpoint, so we pop it and record the width. The right-to-left order means the first j that satisfies a given left endpoint is the largest such j, so once we pop an index we already have its widest ramp.

Algorithm

  1. Build a monotonic decreasing stack of indices by scanning left to right. Push index i onto the stack only if nums[i] is strictly less than nums[stack.top()] (or the stack is empty).
  2. Initialize maxWidth = 0.
  3. Scan from right to left (j = n-1 down to 0):
    • While the stack is not empty and nums[j] >= nums[stack.top()]:
      • Pop i from the stack.
      • Update maxWidth = max(maxWidth, j - i).
  4. Return maxWidth.

Visualization and Code

Loading animation...