Remove One Element to Make the Array Strictly Increasing
EasyPrompt
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.
The array nums is strictly increasing if nums[i - 1] < nums[i] for each index (1 <= i < nums.length).
Example 1:
Input: nums = [1,2,10,5,7]
Output: true
Explanation: By removing 10 at index 2 from nums, it becomes [1,2,5,7].
[1,2,5,7] is strictly increasing, so return true.Example 2:
Input: nums = [2,3,1,2]
Output: false
Explanation:
[3,1,2] is the result of removing the element at index 0.
[2,1,2] is the result of removing the element at index 1.
[2,3,2] is the result of removing the element at index 2.
[2,3,1] is the result of removing the element at index 3.
No resulting array is strictly increasing, so return false.Example 3:
Input: nums = [1,1,1]
Output: false
Explanation: The result of removing any element is [1,1].
[1,1] is not strictly increasing, so return false.
Constraints:
2 <= nums.length <= 10001 <= nums[i] <= 1000
Approaches
2 approaches with complexity analysis and trade-offs.
This approach directly simulates the process described in the problem. We iterate through each element of the array, and for each element, we hypothetically remove it and then check if the remaining part of the array is strictly increasing.
Algorithm
- Loop through each index
ifrom0tonums.length - 1. - In each iteration,
irepresents the index of the element to be removed. - Create a new list,
tempList. - Loop through each index
jfrom0tonums.length - 1. - If
i != j, addnums[j]totempList. - Check if
tempListis strictly increasing. To do this, loop fromk = 1totempList.size() - 1and check iftempList.get(k) <= tempList.get(k-1). If this condition is met, the list is not strictly increasing. - If
tempListis found to be strictly increasing, returntrue. - If the outer loop finishes without finding a valid removal, return
false.
Walkthrough
We can use a loop that iterates from i = 0 to n-1, where n is the length of the array. In each iteration, i represents the index of the element to be removed.
Inside the loop, we construct a new temporary array (or list) that contains all elements of the original array except the one at index i. After constructing the temporary array, we check if it is strictly increasing. This can be done with another loop that iterates through the temporary array, comparing each element with its predecessor.
If we find that a temporary array is strictly increasing, it means we've found a valid removal. We can immediately return true.
If the outer loop completes without finding any such valid removal, it means it's impossible to make the array strictly increasing by removing one element. In this case, we return false.
This also correctly handles the case where the array is already strictly increasing. For example, if nums = [1, 2, 3], removing the last element 3 results in [1, 2], which is strictly increasing, so the function will return true.
import java.util.ArrayList;import java.util.List; class Solution { public boolean canBeIncreasing(int[] nums) { int n = nums.length; for (int i = 0; i < n; i++) { // Create a temporary list without the element at index i List<Integer> tempList = new ArrayList<>(); for (int j = 0; j < n; j++) { if (i != j) { tempList.add(nums[j]); } } // Check if the temporary list is strictly increasing boolean isIncreasing = true; for (int k = 1; k < tempList.size(); k++) { if (tempList.get(k) <= tempList.get(k - 1)) { isIncreasing = false; break; } } if (isIncreasing) { return true; } } return false; }}Complexity
Time
O(N^2), where N is the number of elements in `nums`. The outer loop runs N times. Inside this loop, creating the temporary list takes O(N) time, and checking if it's increasing takes another O(N) time, leading to a total of N * O(N) = O(N^2).
Space
O(N), where N is the number of elements in `nums`. This space is used to store the temporary list in each iteration.
Trade-offs
Pros
Simple to understand and implement.
Directly follows the logic of the problem statement.
Cons
Inefficient in terms of time complexity due to nested loops.
Requires extra space proportional to the input size to create temporary arrays.
This optimal approach avoids the O(N^2) complexity by performing a single pass through the array. The key insight is that for the condition to be met, there can be at most one "violation" (where nums[i] <= nums[i-1]). We can find this violation and check if removing either of the two conflicting elements resolves the issue locally.
Algorithm
- Initialize a counter
violations = 0and an indexp = -1. - Loop through the array from
i = 1ton-1. - If
nums[i] <= nums[i-1], incrementviolationsand store the current indexiinp. - After the loop, check the value of
violations:- If
violations == 0, the array is already strictly increasing. Returntrue. - If
violations > 1, more than one removal is needed. Returnfalse. - If
violations == 1, we have a single dip at indexp. We can fix this if either removingnums[p]ornums[p-1]results in a valid sequence.- Removing
nums[p-1]is valid ifp == 1(it's the first element) or ifnums[p-2] < nums[p]. - Removing
nums[p]is valid ifp == n-1(it's the last element) or ifnums[p-1] < nums[p+1].
- Removing
- If
- If either of the conditions for
violations == 1is met, returntrue. Otherwise, returnfalse.
Walkthrough
The core idea is that if the array can be made strictly increasing by removing one element, there can be at most one index i where nums[i] <= nums[i-1]. If there are two or more such "dips", we would need to remove at least two elements, so we can immediately return false.
We iterate through the array once to find the number of violations and the index of the last violation.
- If we find zero violations, the array is already strictly increasing, so we return
true. - If we find more than one violation, it's impossible to fix the array by removing only one element, so we return
false. - If we find exactly one violation at index
p(i.e.,nums[p] <= nums[p-1]), we have two potential fixes:- Remove
nums[p-1]: The array becomes..., nums[p-2], nums[p], .... This is a valid fix ifnums[p-2] < nums[p]. We must also consider the edge case wherep-1is the first element of the array (p=1), in which case this removal is always a valid local fix. - Remove
nums[p]: The array becomes..., nums[p-1], nums[p+1], .... This is a valid fix ifnums[p-1] < nums[p+1]. We must also consider the edge case wherepis the last element of the array, in which case this removal is always a valid local fix.
- Remove
If either of these two fixes works, we can return true. If the single violation cannot be fixed by either removal, we return false.
class Solution { public boolean canBeIncreasing(int[] nums) { int violations = 0; int p = -1; int n = nums.length; for (int i = 1; i < n; i++) { if (nums[i] <= nums[i - 1]) { violations++; p = i; } } if (violations == 0) { return true; } if (violations > 1) { return false; } // At this point, violations == 1. The violation is at index p. // This means nums[p] <= nums[p-1] // Case 1: Try removing nums[p-1]. The sequence becomes ..., nums[p-2], nums[p], ... // This is valid if p is 1 (i.e., we remove nums[0]) or if nums[p-2] < nums[p]. if (p == 1 || nums[p - 2] < nums[p]) { return true; } // Case 2: Try removing nums[p]. The sequence becomes ..., nums[p-1], nums[p+1], ... // This is valid if p is the last index or if nums[p-1] < nums[p+1]. if (p == n - 1 || nums[p - 1] < nums[p + 1]) { return true; } return false; }}Complexity
Time
O(N), where N is the number of elements in `nums`. We perform a single pass through the array to count violations.
Space
O(1), as we only use a few variables to keep track of violations and indices, regardless of the input size.
Trade-offs
Pros
Highly efficient with linear time complexity.
Space-efficient, using only a constant amount of extra space.
Cons
The logic can be slightly more complex to reason about compared to the direct simulation.
Solutions
Solution
public class Solution { public bool CanBeIncreasing ( int [] nums ) { int n = nums . Length ; bool check ( int k ) { int pre = 0 ; for ( int i = 0 ; i < n ; ++ i ) { if ( i == k ) { continue ; } if ( pre >= nums [ i ]) { return false ; } pre = nums [ i ]; } return true ; } int i = 0 ; while ( i + 1 < n && nums [ i ] < nums [ i + 1 ]) { ++ i ; } return check ( i ) || check ( i + 1 ); } }class Solution {public boolean canBeIncreasing(int[] nums) { int i = 1, n = nums.length; for (; i < n && nums[i - 1] < nums[i]; ++i) ; return check(nums, i - 1) || check(nums, i); }private boolean check(int[] nums, int i) { int prev = Integer.MIN_VALUE; for (int j = 0; j < nums.length; ++j) { if (i == j) { continue; } if (prev >= nums[j]) { return false; } prev = nums[j]; } return true; }}class Solution {public: bool canBeIncreasing(vector<int> &nums) { int i = 1, n = nums.size(); for (; i < n && nums[i - 1] < nums[i]; ++i) ; return check(nums, i - 1) || check(nums, i); } bool check(vector<int> &nums, int i) { int prev = 0; for (int j = 0; j < nums.size(); ++j) { if (i == j) continue; if (prev >= nums[j]) return false; prev = nums[j]; } return true; }};class Solution: def canBeIncreasing(self, nums: List[int]) -> bool: def check(nums, i): prev = - inf for j, num in enumerate(nums): if i == j: continue if prev >= nums[j]: return False prev = nums[j] return True i, n = 1, len(nums) while i < n and nums[i - 1] < nums[i]: i += 1 return check(nums, i - 1) or check(nums, i)Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.