2216. Minimum Deletions to Make Array Beautiful
Problem Description
You have an integer array nums
that is 0-indexed. The goal is to make this array "beautiful" by deleting the minimum number of elements.
An array is considered beautiful if it meets two conditions:
- The array has an even number of elements (length is even)
- For every even index
i
(wherei % 2 == 0
), the element at positioni
must be different from the element at positioni + 1
In other words, if you pair up consecutive elements starting from index 0 (like [nums[0], nums[1]]
, [nums[2], nums[3]]
, etc.), each pair must contain two different values.
When you delete an element from the array:
- All elements to the right of the deleted element shift one position to the left
- All elements to the left remain in their original positions
An empty array is also considered beautiful by definition.
Your task is to find the minimum number of deletions needed to transform the given array into a beautiful array.
For example, if nums = [1, 1, 2, 3, 5]
, you would need to delete 1 element to make it beautiful. After deleting one of the 1s, you get [1, 2, 3, 5]
, which has even length (4) and satisfies the condition that nums[0] ≠ nums[1]
and nums[2] ≠ nums[3]
.
Intuition
Let's think about what makes an array beautiful. We need pairs of elements where each pair has different values. This means we're essentially grouping elements as (element0, element1)
, (element2, element3)
, and so on.
The key insight is that we should process the array greedily from left to right, trying to form valid pairs as we go. Why does greedy work here? Because once we form a valid pair, those elements occupy their positions and we can move on to form the next pair.
As we traverse the array, we're essentially in one of two states:
- Looking for the first element of a pair (at an even position in the final array)
- Looking for the second element of a pair (at an odd position in the final array)
When we find two consecutive elements that are equal, we know they can't form a valid pair. So we delete one of them (increment our deletion counter) and continue looking. When we find two consecutive elements that are different, they can form a valid pair, so we skip both and move to find the next pair.
The clever part of the solution is that we don't actually need to delete elements from the array. We just simulate the process by:
- Moving index by 1 when we "delete" (this element can't be used, so we skip it)
- Moving index by 2 when we find a valid pair (both elements are used)
After processing all elements, we might end up with an odd number of remaining elements. Since a beautiful array must have even length, we need one more deletion if the final length is odd. This is why we add (n - ans) % 2
at the end - if the remaining elements form an odd-length array, we need to delete one more element.
Solution Approach
The solution uses a greedy algorithm that simulates the pairing process without actually modifying the array.
We initialize two variables:
i
: current index for traversing the arrayans
: count of elements to delete
The main algorithm works as follows:
-
Traverse the array with a while loop (
while i < n - 1
):- We stop at
n - 1
because we always check pairs of consecutive elements
- We stop at
-
Check consecutive elements (
nums[i] == nums[i + 1]
):- If they are equal: These two elements cannot form a valid pair in a beautiful array. We "delete" the second element by:
- Incrementing
ans
by 1 (counting this deletion) - Moving
i
forward by 1 (the first element might pair with the next element)
- Incrementing
- If they are different: These two elements can form a valid pair. We:
- Move
i
forward by 2 (skip both elements as they form a valid pair)
- Move
- If they are equal: These two elements cannot form a valid pair in a beautiful array. We "delete" the second element by:
-
Handle odd-length result (
ans += (n - ans) % 2
):- After processing,
n - ans
gives us the number of remaining elements - If this number is odd, we need one more deletion to make the array length even
(n - ans) % 2
equals 1 if odd, 0 if even
- After processing,
Let's trace through an example with nums = [1, 1, 2, 2, 3, 3]
:
i = 0
:nums[0] = 1
,nums[1] = 1
(equal) → delete one,ans = 1
,i = 1
i = 1
:nums[1] = 1
,nums[2] = 2
(different) → valid pair,i = 3
i = 3
:nums[3] = 2
,nums[4] = 3
(different) → valid pair,i = 5
- Loop ends
- Remaining elements:
6 - 1 = 5
(odd) → need one more deletion,ans = 2
The algorithm efficiently finds the minimum deletions in O(n)
time with O(1)
space complexity.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [4, 2, 4, 2, 1, 1]
.
Initial state:
- Array:
[4, 2, 4, 2, 1, 1]
i = 0
,ans = 0
(no deletions yet)- Array length
n = 6
Step 1: Check positions 0 and 1
nums[0] = 4
,nums[1] = 2
- They are different → can form a valid pair
- Skip both elements:
i = 2
- Deletions:
ans = 0
Step 2: Check positions 2 and 3
nums[2] = 4
,nums[3] = 2
- They are different → can form a valid pair
- Skip both elements:
i = 4
- Deletions:
ans = 0
Step 3: Check positions 4 and 5
nums[4] = 1
,nums[5] = 1
- They are equal → cannot form a valid pair
- Delete one element:
ans = 1
- Move to next element:
i = 5
Step 4: Exit loop
i = 5
is not less thann - 1 = 5
, so we exit the while loop
Step 5: Check if remaining array has even length
- Remaining elements:
n - ans = 6 - 1 = 5
- 5 is odd, so we need one more deletion
ans = ans + (5 % 2) = 1 + 1 = 2
Final answer: 2 deletions needed
The resulting beautiful array would be [4, 2, 4, 2]
(after deleting both 1s), which has:
- Even length (4 elements)
nums[0] = 4 ≠ nums[1] = 2
✓nums[2] = 4 ≠ nums[3] = 2
✓
Solution Implementation
1class Solution:
2 def minDeletion(self, nums: List[int]) -> int:
3 # Get the length of the input array
4 n = len(nums)
5
6 # Initialize index pointer and deletion counter
7 index = 0
8 deletions = 0
9
10 # Process pairs of elements in the array
11 while index < n - 1:
12 # Check if current pair would violate the beautiful array condition
13 # (adjacent elements at even positions after deletions would be equal)
14 if nums[index] == nums[index + 1]:
15 # Need to delete one element to fix the violation
16 deletions += 1
17 # Move forward by 1 to skip the problematic element
18 index += 1
19 else:
20 # Current pair is valid, move to the next pair
21 index += 2
22
23 # Ensure the final array has even length
24 # If (n - deletions) is odd, we need one more deletion
25 deletions += (n - deletions) % 2
26
27 return deletions
28
1class Solution {
2 public int minDeletion(int[] nums) {
3 int arrayLength = nums.length;
4 int deletionCount = 0;
5
6 // Iterate through the array to check adjacent pairs
7 for (int currentIndex = 0; currentIndex < arrayLength - 1; currentIndex++) {
8 // Check if current element equals the next element
9 // This represents a violation at an even index position (after deletions)
10 if (nums[currentIndex] == nums[currentIndex + 1]) {
11 // Mark this element for deletion
12 deletionCount++;
13 // Continue checking from the next element
14 } else {
15 // Valid pair found, skip the next element
16 // Move to check the next pair starting from index + 2
17 currentIndex++;
18 }
19 }
20
21 // After deletions, ensure the remaining array has even length
22 // If (original length - deletions) is odd, delete one more element
23 int remainingLength = arrayLength - deletionCount;
24 if (remainingLength % 2 == 1) {
25 deletionCount++;
26 }
27
28 return deletionCount;
29 }
30}
31
1class Solution {
2public:
3 int minDeletion(vector<int>& nums) {
4 int arraySize = nums.size();
5 int deletionCount = 0;
6
7 // Iterate through the array to find elements that need deletion
8 for (int currentIndex = 0; currentIndex < arraySize - 1; ++currentIndex) {
9 // Check if current element equals the next element
10 // This would violate the beautiful array condition at even indices
11 if (nums[currentIndex] == nums[currentIndex + 1]) {
12 // Mark current element for deletion
13 ++deletionCount;
14 } else {
15 // Current pair is valid, skip to the next pair
16 // Increment by 1 more (total of 2) to check the next even position
17 ++currentIndex;
18 }
19 }
20
21 // Ensure the final array has even length
22 // If (arraySize - deletionCount) is odd, delete one more element
23 deletionCount += (arraySize - deletionCount) % 2;
24
25 return deletionCount;
26 }
27};
28
1/**
2 * Finds the minimum number of deletions needed to make the array beautiful.
3 * A beautiful array has even length and no two consecutive elements at even indices are equal.
4 *
5 * @param nums - The input array of numbers
6 * @returns The minimum number of deletions required
7 */
8function minDeletion(nums: number[]): number {
9 const arrayLength: number = nums.length;
10 let deletionCount: number = 0;
11
12 // Iterate through the array checking pairs of elements
13 for (let currentIndex: number = 0; currentIndex < arrayLength - 1; ++currentIndex) {
14 // Check if current element equals the next element (would violate beautiful array rule)
15 if (nums[currentIndex] === nums[currentIndex + 1]) {
16 // Mark this element for deletion
17 ++deletionCount;
18 } else {
19 // Skip the next element as it forms a valid pair with current element
20 ++currentIndex;
21 }
22 }
23
24 // After deletions, if the remaining array has odd length, delete one more element
25 // (arrayLength - deletionCount) gives the remaining elements after deletions
26 deletionCount += (arrayLength - deletionCount) % 2;
27
28 return deletionCount;
29}
30
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array nums
. The algorithm uses a single while loop that traverses through the array once. In each iteration, the index i
advances by either 1 (when nums[i] == nums[i + 1]
) or 2 (when nums[i] != nums[i + 1]
). Since the loop continues while i < n - 1
, each element is visited at most once, resulting in linear time complexity.
The space complexity is O(1)
. The algorithm only uses a constant amount of extra space for variables n
, i
, and ans
, regardless of the input size. No additional data structures are created that scale with the input, making the space usage constant.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Index Tracking Logic
The Pitfall: Many developers mistakenly think the index
variable represents the actual index in the modified array after deletions. This leads to confusion about why we increment index
by 1 when we find equal elements.
Why it happens: The variable name index
suggests it's tracking positions in the final array, but it's actually tracking positions in the original array while simulating the pairing process.
The Fix: Rename the variable to better reflect its purpose and add clear comments:
def minDeletion(self, nums: List[int]) -> int:
n = len(nums)
original_pos = 0 # Position in the ORIGINAL array
deletions = 0
virtual_pos = 0 # Track position in the virtual "result" array
while original_pos < n - 1:
# Check if this element would be at an even position in result
if (virtual_pos % 2 == 0):
if nums[original_pos] == nums[original_pos + 1]:
# Delete current element, don't increment virtual position
deletions += 1
original_pos += 1
else:
# Valid pair found
original_pos += 2
virtual_pos += 2
else:
original_pos += 1
virtual_pos += 1
# Check if result has even length
deletions += (n - deletions) % 2
return deletions
2. Forgetting the Final Even-Length Check
The Pitfall: Developers often forget the line deletions += (n - deletions) % 2
, assuming that the main loop handles everything.
Example of the bug:
# WRONG: Missing the final check
def minDeletion(self, nums: List[int]) -> int:
n = len(nums)
index = 0
deletions = 0
while index < n - 1:
if nums[index] == nums[index + 1]:
deletions += 1
index += 1
else:
index += 2
return deletions # Bug: doesn't ensure even length!
For input [1, 2, 3]
, this would return 0, but the correct answer is 1 (we need to delete one element to make the length even).
3. Off-by-One Error in Loop Condition
The Pitfall: Using while index < n
instead of while index < n - 1
, causing an index out of bounds error when checking nums[index + 1]
.
The Fix: Always ensure the loop condition prevents accessing nums[index + 1]
when index
is at the last position:
# Correct loop condition while index < n - 1: # Ensures nums[index + 1] is valid if nums[index] == nums[index + 1]: # ...
4. Incorrectly Simulating the Deletion Process
The Pitfall: Some developers try to actually modify the array or create a new array to track deletions, which complicates the logic and increases space complexity:
# INEFFICIENT AND ERROR-PRONE
def minDeletion(self, nums: List[int]) -> int:
result = []
deletions = 0
for num in nums:
if len(result) % 2 == 0:
result.append(num)
elif result[-1] != num:
result.append(num)
else:
deletions += 1
if len(result) % 2 == 1:
deletions += 1
return deletions
This approach is harder to debug and understand compared to the greedy simulation approach.
How many times is a tree node visited in a depth first search?
Recommended Readings
Stack Intro Imagine you have a pile of books on your desk If you want to add a new book you place it on top If you want to read a book you take it from the top And if you simply want to see which book is on the top you
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!