Remove One Element to Make the Array Strictly Increasing
Given a 0-indexed integer array nums, return true if it can be made strictly increasing after removing exactly one element, or false otherwise. If the array is already strictly increasing, return true.
This is LeetCode 1909: Remove One Element to Make the Array Strictly Increasing, an easy problem that is trickier than it first appears. The key is figuring out which element to remove when you find a violation, and handling the edge cases cleanly.
Why this problem matters
This problem teaches you how to handle violations in ordered sequences. The core skill is not just finding where an array breaks its ordering, but reasoning about which element to remove and verifying that the fix actually works. That kind of careful edge-case handling shows up constantly in array problems.
It also reinforces a useful pattern: when you find something wrong, you often have a small number of candidate fixes to try. Instead of brute-forcing every possibility, you narrow down to just the relevant candidates and check them.
The approach: find the violation, try two removals
Here is the idea. Scan the array from left to right, comparing each adjacent pair. The moment you find an index i where nums[i] >= nums[i + 1], you have found the violation point. Now there are only two elements that could be removed to fix it: nums[i] (the left element) or nums[i + 1] (the right element). Try removing each one and check if the resulting array is strictly increasing. If either removal works, return true. If neither does, return false.
If you scan the entire array without finding any violation, the array is already strictly increasing, so return true.
Why only two candidates? Because the violation is between nums[i] and nums[i + 1]. Removing any other element cannot fix this specific violation. So you only need to check two possibilities, not all n.
def can_be_increasing(nums):
def is_strictly_increasing(arr, skip):
prev = -1
for j in range(len(arr)):
if j == skip:
continue
if arr[j] <= prev:
return False
prev = arr[j]
return True
for i in range(len(nums) - 1):
if nums[i] >= nums[i + 1]:
return is_strictly_increasing(nums, i) or is_strictly_increasing(nums, i + 1)
return True
Step 1: Scan for the first violation. Compare each pair.
1 < 2? Yes. 2 < 10? Yes. Keep scanning.
Step 2: Found it. nums[2] >= nums[3] (10 >= 5). Violation at i = 2.
The first place the array stops increasing is between index 2 and index 3.
Step 3: Try removing nums[2] (the left element at the violation).
Removing 10 gives [1, 2, 5, 7]. Check: is that strictly increasing?
Step 4: Check [1, 2, 5, 7]. Is it strictly increasing?
1 < 2 < 5 < 7. Yes! Removing index 2 works. Return True.
What if removing the left element does not work?
Alternative: array [1, 2, 10, 5, 7] with the right removal.
We could also try removing nums[3] = 5, giving [1, 2, 10, 7].
Check [1, 2, 10, 7]. Is it strictly increasing?
1 < 2 < 10, but 10 > 7. No. This removal does not fix it.
A case where removing the right element works instead:
Example: nums = [1, 4, 2, 3]. Violation at i = 1 (4 >= 2).
4 > 2 is the first violation. Try removing the left element (4) or the right element (2).
Try removing nums[1] = 4. Check [1, 2, 3].
1 < 2 < 3. Strictly increasing. Return True.
A helper function approach
You can also write this with a cleaner separation of concerns. Define a helper that counts the number of violations. If there are zero violations, the array is already good. If there is one, try the two candidate removals. If there are two or more, no single removal can fix it.
Here is an alternative that uses a helper to check if an array (with one index skipped) is strictly increasing:
def can_be_increasing(nums):
def check(skip):
prev = -float('inf')
for i in range(len(nums)):
if i == skip:
continue
if nums[i] <= prev:
return False
prev = nums[i]
return True
for i in range(len(nums) - 1):
if nums[i] >= nums[i + 1]:
return check(i) or check(i + 1)
return True
Both solutions follow the same logic. Find the first violation, try removing one of the two elements involved, and verify the result.
A common mistake is to just check whether there is at most one violation and return true. That does not work. Consider [1, 2, 10, 5, 7]. There is one violation (10 to 5), but you need to verify that removing the right element actually produces a valid array. Simply counting violations is not enough because removing one element can create a new violation with its neighbors.
Complexity analysis
| Approach | Time | Space |
|---|---|---|
| Brute force (try removing each) | O(n^2) | O(n) |
| Find violation + check | O(n) | O(1) |
Time: O(n). You scan the array once to find the violation. Then you do at most two more O(n) passes to verify the candidate removals. That is 3n in the worst case, which is still O(n).
Space: O(1). You never build a new array. The check helper uses a skip index to virtually remove an element without allocating extra space.
Building blocks
This problem uses two reusable patterns:
Single-pass array scanning. Walk through the array comparing adjacent elements. This is the same scan you use in problems like checking if an array is sorted, finding a peak element, or detecting duplicates. The pattern is always the same: iterate, compare nums[i] with nums[i + 1] (or with a running value), and act on the first interesting condition.
Greedy candidate testing. When you find a problem, you do not try every possible fix. You identify the small set of candidates that could actually help (here, just two elements), test each one, and return the result. This greedy narrowing shows up in many problems where brute force would check every option but you can reason about which options are worth checking.
Edge cases
Make sure your solution handles these:
- Already strictly increasing like
[1, 2, 3, 4]: no violation found, returntrue. - Violation at the start like
[5, 1, 2, 3]: removingnums[0]gives[1, 2, 3], which works. - Violation at the end like
[1, 2, 3, 1]: removingnums[3]gives[1, 2, 3], which works. - Two violations like
[3, 1, 4, 2]: violations at index 0 and index 2. Removing either element at the first violation does not fix the second. Returnfalse. - Length 2 like
[2, 1]: removing either element leaves a single-element array, which is trivially increasing. Returntrue. - Equal adjacent elements like
[1, 1, 2, 3]:nums[0] >= nums[1]is a violation (equal is not strictly increasing). Removingnums[0]gives[1, 2, 3]. Returntrue.
From understanding to recall
You have read through the solution and it makes sense. Find the violation, try two removals, verify. Simple enough. But the details matter: using >= instead of > for the violation check, handling the skip index correctly in the helper, remembering to return true when no violation exists. These are the things that trip you up under interview pressure if you have not practiced writing the code from scratch.
Spaced repetition closes that gap. You practice the violation-scan-and-verify pattern at increasing intervals until it is automatic. After a few rounds, you see "can you fix the array by removing one element?" and the structure flows out without hesitation.
Related posts
- Best Time to Buy and Sell Stock - another single-pass array problem tracking a running condition
- Longest Increasing Subsequence - the harder version where you find the longest increasing sequence
- Jump Game - another greedy array scan deciding feasibility