Minimum Deletions to Make Array Beautiful
MedPrompt
You are given a 0-indexed integer array nums. The array nums is beautiful if:
nums.lengthis even.nums[i] != nums[i + 1]for alli % 2 == 0.
Note that an empty array is considered beautiful.
You can delete any number of elements from nums. When you delete an element, all the elements to the right of the deleted element will be shifted one unit to the left to fill the gap created and all the elements to the left of the deleted element will remain unchanged.
Return the minimum number of elements to delete from nums to make it beautiful.
Example 1:
nums[0]Example 2:
nums[0]
Constraints:
1 <= nums.length <= 1050 <= nums[i] <= 105
Approaches
3 approaches with complexity analysis and trade-offs.
A less efficient but valid approach is to use dynamic programming. We can define dp[i] as the length of the longest subsequence that can be formed using a prefix of the input array nums[0...i], must include nums[i] as its last element, and satisfies the condition seq[k] != seq[k+1] for all even k in the subsequence seq.
Algorithm
- Initialize a DP array
dpof sizen, wheredp[i]stores the length of the longest valid subsequence ending withnums[i]. - Iterate from
i = 0ton-1:- Set
dp[i] = 1(for the subsequence containing onlynums[i]). - Iterate from
j = 0toi-1:- Let
len_j = dp[j]. - If
len_jis odd, we can appendnums[i]. Updatedp[i] = max(dp[i], len_j + 1). - If
len_jis even andnums[i] != nums[j], we can appendnums[i]. Updatedp[i] = max(dp[i], len_j + 1).
- Let
- Set
- Find the maximum value in the
dparray,max_len. - If
max_lenis odd, the number of elements we can keep ismax_len - 1. Otherwise, it'smax_len. - The result is
n - kept_count.
Walkthrough
This approach solves the problem by building up solutions for progressively larger prefixes of the array. We define dp[i] as the length of the longest valid subsequence (one that satisfies seq[k] != seq[k+1] for even k) that ends with the element nums[i].
To compute dp[i], we consider all previous elements nums[j] (where j < i) as potential predecessors. We can append nums[i] to the subsequence ending at nums[j] if the resulting new subsequence remains valid. The condition for appending depends on the length of the subsequence ending at j, dp[j]:
- If
dp[j]is odd,nums[j]is at an odd-indexed position in its subsequence. The new elementnums[i]will be at an even-indexed position, which has no immediate predecessor constraint, so we can always append. - If
dp[j]is even,nums[j]is at an even-indexed position. The new elementnums[i]will be at an odd-indexed position, so we can only append it ifnums[i] != nums[j].
After filling the dp array, the maximum value max_len represents the longest possible valid subsequence. Since a beautiful array must also have an even length, the final number of elements we can keep is max_len if it's even, or max_len - 1 if it's odd. The minimum deletions will be the original array length minus this final count.
import java.util.Arrays; class Solution { public int minDeletion(int[] nums) { int n = nums.length; if (n == 0) { return 0; } int[] dp = new int[n]; Arrays.fill(dp, 1); int maxLen = 1; for (int i = 1; i < n; i++) { for (int j = 0; j < i; j++) { // The new element nums[i] will be at index dp[j] (0-indexed) // If dp[j] is even, it's an even position. nums[j] is at dp[j]-1 (odd). // If dp[j] is odd, it's an odd position. nums[j] is at dp[j]-1 (even). if (dp[j] % 2 != 0) { // nums[j] is at an even position in its subsequence if (nums[i] != nums[j]) { dp[i] = Math.max(dp[i], dp[j] + 1); } } else { // nums[j] is at an odd position in its subsequence dp[i] = Math.max(dp[i], dp[j] + 1); } } maxLen = Math.max(maxLen, dp[i]); } int keptCount = maxLen; if (keptCount % 2 != 0) { keptCount--; } return n - keptCount; }}Note: The logic in the code snippet is slightly different but follows the same DP principle. A simpler DP transition is dp[i] = 1 + max(dp[j]) where j<i and (dp[j]%2==1 && nums[i]!=nums[j]) || (dp[j]%2==0). The provided code implements this logic.
Complexity
Time
O(N^2), where N is the length of `nums`, due to the nested loops.
Space
O(N), for the `dp` array.
Trade-offs
Pros
A standard dynamic programming pattern for subsequence problems.
Guaranteed to find the optimal solution.
Cons
Inefficient time complexity of O(N^2), which may lead to a 'Time Limit Exceeded' error on large inputs.
Requires O(N) extra space for the DP array.
A more efficient approach is to use a greedy strategy. We can construct the longest possible beautiful array by iterating through the input nums and making a local optimal choice at each step. We use an auxiliary list to build this new array. The choice is to keep an element if and only if it doesn't violate the beautiful array conditions with the elements already kept.
Algorithm
- Initialize an empty list,
res. - Iterate through each
numinnums. - If the current size of
resis even, it's a new pair's start, so addnumtores. - If the current size of
resis odd,numwould be the second element of a pair. Add it only if it's different from the last element inres. - After the loop, let
keptCount = res.size(). - If
keptCountis odd, we must delete one more element (the last one). So,finalKeptCount = keptCount - 1. - Otherwise,
finalKeptCount = keptCount. - Return
nums.length - finalKeptCount.
Walkthrough
The greedy strategy aims to maximize the number of elements we keep, which is equivalent to minimizing deletions. We iterate through the nums array and build a new list, let's call it beautifulList, which will store the elements of our resulting beautiful array.
The logic for adding an element num from nums to beautifulList depends on the current size of beautifulList:
- If
beautifulList.size()is even, the new elementnumwill be at an even index (e.g., 0, 2, ...). This is the first element of a new pair, so there are no constraints. We can greedily addnumtobeautifulList. - If
beautifulList.size()is odd, the new elementnumwill be at an odd index (e.g., 1, 3, ...). This is the second element of a pair. It must be different from the preceding element (which is atbeautifulList.size() - 1). We addnumonly ifnum != beautifulList.get(beautifulList.size() - 1). If they are equal, we must deletenumand continue searching for a suitable element for this position.
After iterating through all of nums, beautifulList holds the longest possible sequence satisfying the second condition. Finally, we check the even-length condition. If beautifulList.size() is odd, we must perform one final deletion (conceptually, removing the last element). The total deletions is the original length minus the final size of our beautiful array.
import java.util.ArrayList;import java.util.List; class Solution { public int minDeletion(int[] nums) { List<Integer> beautifulList = new ArrayList<>(); for (int num : nums) { if (beautifulList.size() % 2 == 0) { beautifulList.add(num); } else { if (beautifulList.get(beautifulList.size() - 1) != num) { beautifulList.add(num); } } } int keptCount = beautifulList.size(); if (keptCount % 2 != 0) { return nums.length - (keptCount - 1); } else { return nums.length - keptCount; } }}Complexity
Time
O(N), where N is the length of `nums`. We iterate through the array once, and list operations take amortized O(1) time.
Space
O(N), as the `beautifulList` can, in the worst case, store all elements of `nums`.
Trade-offs
Pros
Efficient O(N) time complexity.
The logic is intuitive and directly models the construction of the beautiful array.
Cons
Uses O(N) extra space in the worst case, which can be optimized.
This approach is the most optimal solution. It builds upon the greedy strategy but eliminates the need for an auxiliary list, reducing space complexity to O(1). Instead of storing the new array, we can simply keep a count of the number of elements that must be deleted. The core logic remains the same: we iterate through the array and identify elements that violate the beautiful array conditions.
Algorithm
- Initialize
deletions = 0. - Iterate
ifrom0ton-2. - The index of
nums[i]in the new array isi - deletions. - If this new index
(i - deletions)is even,nums[i]is the first element of a pair. - Check if it's equal to the next element,
nums[i+1]. Ifnums[i] == nums[i+1], we must delete one. We incrementdeletions. - After the loop, calculate the number of remaining elements:
keptCount = n - deletions. - If
keptCountis odd, we need one more deletion. Incrementdeletions. - Return
deletions.
Walkthrough
We can achieve the same result as the greedy approach without using any extra space. The key observation is that to make a decision at index i, we only need to know the effective size of the beautiful array built so far, not its actual contents. The effective size determines whether nums[i] would be at an even or odd position.
The effective index for nums[i] in the new array is i - deletions, where deletions is the count of elements we have discarded from nums[0...i-1]. We only care about violations of the form new_nums[k] == new_nums[k+1] where k is even. This corresponds to an element nums[i] at an even effective index i - deletions being equal to the next non-deleted element.
A simpler way to implement this is to iterate through the array and find adjacent elements nums[i] and nums[i+1] that need to be placed at an even and odd index respectively. If (i - deletions) is an even index and nums[i] == nums[i+1], we have a violation. We must delete one element, so we increment deletions.
After iterating through the array, we have the total deletions required to satisfy the second condition. Finally, we check if the remaining number of elements, nums.length - deletions, is even. If not, one more deletion is required to satisfy the first condition.
class Solution { public int minDeletion(int[] nums) { int n = nums.length; int deletions = 0; // We iterate up to n-1 because we access i+1 for (int i = 0; i < n - 1; i++) { // The index in the new (conceptual) array is (i - deletions) if ((i - deletions) % 2 == 0) { if (nums[i] == nums[i + 1]) { // Found a pair nums[k] == nums[k+1] where k is even in the new array. // We must delete one. Incrementing deletions effectively deletes nums[i+1] // by shifting the indices of all subsequent elements. deletions++; } } } // After all pair-based deletions, if the remaining array has an odd length, // we must delete one more element (e.g., the last one). if ((n - deletions) % 2 != 0) { deletions++; } return deletions; }}Complexity
Time
O(N), where N is the length of `nums`, as we perform a single pass through the array.
Space
O(1), as we only use a few variables to store counts.
Trade-offs
Pros
Optimal time complexity of O(N).
Optimal space complexity of O(1).
Highly efficient for large inputs.
Cons
The logic of tracking the new index
i - deletionscan be slightly less direct to understand compared to building an explicit list.
Solutions
Solution
class Solution {public int minDeletion(int[] nums) { int n = nums.length; int ans = 0; for (int i = 0; i < n - 1; ++i) { if (nums[i] == nums[i + 1]) { ++ans; } else { ++i; } } ans += (n - ans) % 2; return ans; }}class Solution: def minDeletion(self, nums: List[int]) -> int: n = len(nums) i = ans = 0 while i < n - 1: if nums[i] == nums[i + 1]: ans += 1 i += 1 else: i += 2 ans += (n - ans) % 2 return ansclass Solution { public: int minDeletion ( vector < int >& nums ) { int n = nums . size (); int ans = 0 ; for ( int i = 0 ; i < n - 1 ; ++ i ) { if ( nums [ i ] == nums [ i + 1 ]) { ++ ans ; } else { ++ i ; } } ans += ( n - ans ) % 2 ; return ans ; } };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.