Minimum Operations to Make the Array Increasing: Greedy Pass Pattern
You are given a 0-indexed integer array nums. In one operation, you can choose any element and increment it by 1. Return the minimum number of operations needed to make nums strictly increasing.
This is LeetCode 1827: Minimum Operations to Make the Array Increasing, an easy problem that demonstrates the single-pass greedy scanning pattern. You never need to look back or reconsider earlier decisions. One pass from left to right, tracking what each element needs to become, gives you the answer.
Why this problem matters
This problem is a clean entry point into the greedy forward scan family. You process elements left to right, and at each index you make a local decision that is guaranteed to be globally optimal. No dynamic programming table, no backtracking, no sorting. Just one variable tracking the previous value and a running counter.
The same pattern appears in harder problems like Candy and Jump Game, where you scan in one direction and make greedy choices at each step. Mastering this easy version builds the reflex you need for those tougher variants.
The key insight: track the minimum required value
Scan left to right. Maintain a variable prev that holds the value the last element was set to. For each new element:
- If
nums[i]is already greater thanprev, it is fine as-is. Setprev = nums[i]. - If
nums[i]is less than or equal toprev, you must bump it toprev + 1. The cost is(prev + 1) - nums[i]operations. Setprev = prev + 1.
This works because you can only increment, never decrement. Each element only depends on the one before it. Making it exactly prev + 1 when it is too small is always the cheapest move. Any value larger than prev + 1 would cost more and provide no benefit to later elements.
The solution
def min_operations(nums):
ops = 0
prev = nums[0]
for i in range(1, len(nums)):
if nums[i] <= prev:
prev += 1
ops += prev - nums[i]
else:
prev = nums[i]
return ops
Here is what each piece does:
opsaccumulates the total number of increment operations.prevtracks the value of the previous element after any adjustments.- Start by setting
prev = nums[0]. The first element never needs to change because there is nothing before it to compare against. - For each subsequent element, check if it is less than or equal to
prev. If so, it must becomeprev + 1. Add the difference toopsand updateprev. - If the element is already greater than
prev, no operations are needed. Just updateprevto the current element's value. - After the loop,
opsholds the total minimum operations.
The logic is greedy because at each index you do the minimum possible work to satisfy the strictly increasing constraint. You never overshoot, and you never need to revisit earlier elements.
The variable prev does double duty. It tracks both what the last element became and what the current element must exceed. This means you do not need to modify the original array or allocate any extra storage.
Walking through it step by step
Let's trace through nums = [1, 1, 1]. We maintain ops = 0 and prev = 1 (the first element).
Step 1: index 0
First element. Set prev = 1. No operations needed.
Step 2: index 1
nums[1] = 1, but it must be at least prev + 1 = 2. Increment once. prev = 2.
Step 3: index 2
nums[2] = 1, but it must be at least prev + 1 = 3. Increment twice. prev = 3.
The final answer is 3. Each element that was too small got bumped to exactly one more than the previous, and no extra work was done.
Complexity analysis
| Metric | Value |
|---|---|
| Time | O(n), single pass through the array |
| Space | O(1), only two variables (ops and prev) |
This is optimal. You must look at every element at least once to know whether it needs incrementing, so O(n) time is the lower bound. And O(1) space means you are not creating any auxiliary data structures.
Building blocks
This problem is built on two reusable patterns that CodeBricks drills independently.
1. Left-to-right greedy scan
The pattern of processing an array from left to right, making a locally optimal decision at each index:
prev = initial_value
for i in range(1, len(arr)):
if arr[i] violates constraint relative to prev:
fix arr[i] with minimum cost
update prev
else:
prev = arr[i]
In Minimum Operations, the constraint is arr[i] > prev. In Jump Game, the constraint is reachability. In Best Time to Buy and Sell Stock, the constraint is tracking the minimum seen so far. The skeleton is identical: scan forward, maintain one piece of state, and make a greedy choice at each step.
2. Gap accumulation
The pattern of computing the total cost by summing up the gap between what you have and what you need:
total_cost = 0
for each element:
gap = required_value - actual_value
total_cost += gap
In this problem, the gap is (prev + 1) - nums[i] when the element is too small. In problems like Minimum Cost to Move Chips, the gap is the distance each chip needs to move. The idea is the same: measure how far each item is from where it needs to be, and sum up the costs.
Edge cases
Before submitting, make sure your solution handles these:
- Already increasing
[1, 2, 3, 4]: every element is already greater than the previous. Zero operations needed. - All equal
[5, 5, 5, 5]: each element after the first must be bumped. Total ops = 0 + 1 + 2 + 3 = 6. - Decreasing array
[4, 3, 2, 1]: the worst case for a given set of values. Each element needs more bumping than the last. For this input, [4, 5, 6, 7] costs 0 + 2 + 4 + 6 = 12 operations. - Single element
[42]: only one element, nothing to compare. Returns 0. - Two elements, already increasing
[1, 2]: no operations needed. Returns 0. - Two elements, equal
[3, 3]: bump the second to 4. Returns 1.
The greedy solution handles all of these without special-case logic.
From understanding to recall
You have read the greedy solution and it makes sense. One loop, one variable, one accumulator. But can you write it from scratch under interview pressure without looking at it?
The details matter: initializing prev to nums[0], checking nums[i] <= prev (not just <), and computing the cost as prev + 1 - nums[i] before updating prev. These are small things, but getting any one of them wrong produces a bug.
Spaced repetition closes that gap. You practice writing the left-to-right greedy scan from scratch at increasing intervals. After a few rounds, the pattern is automatic. You see "make array strictly increasing with minimum increments" and the code flows out without hesitation.
The greedy forward scan pattern is one of roughly 60 reusable building blocks that cover hundreds of LeetCode problems. Learning them individually and drilling them with spaced repetition is far more effective than grinding random problems and hoping they stick.
Related posts
- Jump Game - Greedy forward scanning pattern
- Best Time to Buy and Sell Stock - Single-pass greedy tracking
- Candy - Greedy array adjustment with neighbor constraints
CodeBricks breaks the minimum operations LeetCode problem into its greedy scan and gap accumulation building blocks, then drills them independently with spaced repetition. You type each piece from scratch until the pattern is automatic. When a greedy array adjustment question shows up in your interview, you do not think about it. You just write it.