You are given an integer array nums. In one operation, you can select a subarray and replace it with a single element equal to its maximum value.
Return the maximum possible size of the array after performing zero or more operations such that the resulting array is non-decreasing.
Example 1:
Input: nums = [4,2,5,3,5]
Output: 3
Explanation:
One way to achieve the maximum size is:
nums[1..2] = [2, 5] with 5 → [4, 5, 3, 5].nums[2..3] = [3, 5] with 5 → [4, 5, 5].The final array [4, 5, 5] is non-decreasing with size 3.
Example 2:
Input: nums = [1,2,3]
Output: 3
Explanation:
No operation is needed as the array [1,2,3] is already non-decreasing.
Constraints:
1 <= nums.length <= 2 * 1051 <= nums[i] <= 2 * 105Problem Overview: You are given an integer array. In each round, remove every element nums[i] where nums[i] < nums[i-1]. Continue until the array becomes non-decreasing. The task is to compute how many rounds are required.
Approach 1: Direct Simulation (O(n²) time, O(n) space)
The most straightforward idea is to simulate the process exactly as described. Iterate through the array and remove elements that violate the non‑decreasing condition (nums[i] < nums[i-1]). After a pass, rebuild the array and repeat until no more removals happen. Each round scans the array and potentially shifts elements, which can cost O(n). In the worst case you perform up to O(n) rounds, leading to O(n²) time and O(n) auxiliary space for rebuilding the array. This approach is useful for understanding the mechanics but will time out for large inputs.
Approach 2: Monotonic Stack + Greedy (O(n) time, O(n) space)
The key observation: an element disappears only because there is a larger value somewhere to its left that survives longer. Instead of simulating rounds, track how long each element takes before being removed. Use a decreasing monotonic stack that stores pairs of (value, stepsToDie). Traverse the array from left to right. While the stack top has a value ≤ current value, pop it and track the maximum removal time seen so far. If the stack becomes empty, the current element never gets removed. Otherwise it dies one round after the maximum popped delay. Push the element with its computed delay.
This greedy structure works because smaller elements to the left cannot protect the current element from a larger survivor further left. Each element is pushed and popped at most once, giving O(n) time and O(n) space. The approach relies heavily on patterns from stack processing and array traversal.
Approach 3: DP with Monotonic Stack Variant (O(n) time, O(n) space)
Another way to view the same idea is dynamic programming. Define dp[i] as the number of rounds before nums[i] gets removed. While iterating, pop stack elements smaller or equal to the current value and propagate the maximum dp value encountered. If a larger element remains on the stack, set dp[i] = maxDelay + 1. Otherwise dp[i] = 0. The global answer is the maximum dp[i]. This formulation clarifies why the stack approach works: each element inherits delay from blockers on its left.
Recommended for interviews: Interviewers expect the monotonic stack solution. The brute force simulation demonstrates understanding of the removal process, but the O(n) stack approach shows strong pattern recognition with greedy reasoning and stack-based array processing.
Python
Java
C++
Go
TypeScript
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Direct Simulation | O(n²) | O(n) | Useful for understanding the process or when constraints are very small |
| Monotonic Stack Greedy | O(n) | O(n) | Best general solution for large arrays; each element processed once |
| DP + Stack Variant | O(n) | O(n) | Helpful when reasoning about per-element removal timing |
3523. Make Array Non-decreasing (Leetcode Medium) • Programming Live with Larry • 367 views views
Watch 3 more video solutions →Practice Make Array Non-decreasing with our built-in code editor and test cases.
Practice on FleetCode