2091. Removing Minimum and Maximum From Array
Problem Description
You have an array nums
containing distinct integers at different positions (0-indexed). Your task is to remove both the minimum and maximum values from this array.
You can only remove elements in two ways:
- Delete from the front of the array (removing the first element)
- Delete from the back of the array (removing the last element)
You need to find the minimum number of deletions required to remove both the smallest and largest elements from the array.
For example, if you have an array like [2, 10, 7, 5, 4, 1, 8, 6]
:
- The minimum value is
1
(at index 5) - The maximum value is
10
(at index 1)
You could remove them by:
- Deleting from the front until both are removed (6 deletions)
- Deleting from the back until both are removed (7 deletions)
- Deleting from both ends strategically (2 from front to get
10
, then 3 from back to get1
, totaling 5 deletions)
The solution finds the positions of both minimum and maximum elements, then calculates three possible strategies:
- Delete everything from the front up to and including the rightmost target element
- Delete everything from the back up to and including the leftmost target element
- Delete from the front to get one element and from the back to get the other
The answer is the minimum among these three approaches.
Intuition
Since we need to remove both the minimum and maximum elements, and we can only delete from the front or back, we need to think about where these two elements are positioned in the array.
The key insight is that there are only three reasonable strategies to remove both elements:
-
Delete only from the front: Keep deleting from the front until both elements are gone. This means we delete up to the position of whichever element is further from the front.
-
Delete only from the back: Keep deleting from the back until both elements are gone. This means we delete up to the position of whichever element is further from the back.
-
Delete from both ends: Delete from the front to remove one element, and from the back to remove the other. This works best when one element is closer to the front and the other is closer to the back.
Let's visualize this with positions. If we have the minimum at position mi
and maximum at position mx
, and assume mi < mx
(the left one comes first), then:
- Strategy 1: Delete
mx + 1
elements from the front (since we need to reach indexmx
) - Strategy 2: Delete
len(nums) - mi
elements from the back (to reach indexmi
from the back) - Strategy 3: Delete
mi + 1
from the front to get the minimum, pluslen(nums) - mx
from the back to get the maximum
The optimal solution is simply the minimum of these three values. By ensuring mi < mx
through swapping if needed, we can consistently apply these formulas regardless of which element (min or max) appears first in the array.
This greedy approach works because once we commit to a deletion strategy, we want to minimize the total deletions needed to reach both target elements.
Learn more about Greedy patterns.
Solution Approach
The implementation follows a straightforward approach with these steps:
Step 1: Find the indices of minimum and maximum elements
We iterate through the array once, keeping track of the indices of the minimum and maximum values:
mi = mx = 0
for i, num in enumerate(nums):
if num < nums[mi]:
mi = i
if num > nums[mx]:
mx = i
This uses two variables mi
and mx
to store the indices. We compare each element with the current minimum and maximum values and update the indices accordingly.
Step 2: Ensure the minimum index comes before the maximum index
For consistency in our calculations, we swap the indices if needed:
if mi > mx: mi, mx = mx, mi
After this swap, mi
represents the leftmost target element's index, and mx
represents the rightmost target element's index.
Step 3: Calculate the minimum deletions using three strategies
We compute the cost of each strategy and return the minimum:
return min(mx + 1, len(nums) - mi, mi + 1 + len(nums) - mx)
Breaking down the three strategies:
mx + 1
: Delete from the front until we reach the rightmost target (indexmx
). We needmx + 1
deletions since arrays are 0-indexed.len(nums) - mi
: Delete from the back until we reach the leftmost target (indexmi
). The number of elements from indexmi
to the end islen(nums) - mi
.mi + 1 + len(nums) - mx
: Delete from both ends - remove the leftmost target from the front (mi + 1
deletions) and the rightmost target from the back (len(nums) - mx
deletions).
Time Complexity: O(n)
where n is the length of the array, as we traverse it once to find min and max indices.
Space Complexity: O(1)
as we only use a constant amount of extra space for storing indices.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with a concrete example: nums = [5, 3, 8, 1, 9, 2]
Step 1: Find minimum and maximum elements
- Traverse the array to locate:
- Minimum value:
1
at index3
- Maximum value:
9
at index4
- Minimum value:
So mi = 3
and mx = 4
.
Step 2: Ensure correct ordering
Since mi < mx
(3 < 4), no swap is needed. We have:
- Leftmost target at index
3
- Rightmost target at index
4
Step 3: Calculate three strategies
Let's visualize the array with our targets marked:
[5, 3, 8, 1, 9, 2] β β mi mx
Now we calculate each strategy:
Strategy 1 - Delete only from front:
- Need to reach index
4
(the rightmost target) - Deletions required:
mx + 1 = 4 + 1 = 5
- Process: Delete [5], [3], [8], [1], [9] β Array becomes [2]
Strategy 2 - Delete only from back:
- Need to reach index
3
(the leftmost target) from the back - Deletions required:
len(nums) - mi = 6 - 3 = 3
- Process: Delete [2], [9], [1] β Array becomes [5, 3, 8]
Strategy 3 - Delete from both ends:
- Delete from front to reach index
3
:mi + 1 = 3 + 1 = 4
deletions - Delete from back to reach index
4
:len(nums) - mx = 6 - 4 = 2
deletions - Total:
4 + 2 = 6
deletions - Process: Delete [5], [3], [8], [1] from front, then [2], [9] from back β Array becomes empty
Final Answer:
min(5, 3, 6) = 3
The optimal strategy is to delete only from the back, requiring just 3 deletions.
Solution Implementation
1class Solution:
2 def minimumDeletions(self, nums: List[int]) -> int:
3 # Find indices of minimum and maximum elements
4 min_index = 0
5 max_index = 0
6
7 # Iterate through array to find positions of min and max elements
8 for i, num in enumerate(nums):
9 if num < nums[min_index]:
10 min_index = i
11 if num > nums[max_index]:
12 max_index = i
13
14 # Ensure min_index is always less than or equal to max_index
15 # This simplifies the calculation logic
16 if min_index > max_index:
17 min_index, max_index = max_index, min_index
18
19 # Calculate minimum deletions using three strategies:
20 # 1. Delete from left until both elements are removed (max_index + 1)
21 # 2. Delete from right until both elements are removed (len(nums) - min_index)
22 # 3. Delete from left to remove first element, then from right to remove second
23 # (min_index + 1) + (len(nums) - max_index)
24 return min(
25 max_index + 1, # Strategy 1: all from left
26 len(nums) - min_index, # Strategy 2: all from right
27 min_index + 1 + len(nums) - max_index # Strategy 3: from both ends
28 )
29
1class Solution {
2 public int minimumDeletions(int[] nums) {
3 // Find indices of minimum and maximum elements
4 int minIndex = 0;
5 int maxIndex = 0;
6 int arrayLength = nums.length;
7
8 // Iterate through array to find min and max element positions
9 for (int i = 0; i < arrayLength; i++) {
10 if (nums[i] < nums[minIndex]) {
11 minIndex = i;
12 }
13 if (nums[i] > nums[maxIndex]) {
14 maxIndex = i;
15 }
16 }
17
18 // Ensure minIndex is always less than or equal to maxIndex for easier calculation
19 if (minIndex > maxIndex) {
20 int temp = maxIndex;
21 maxIndex = minIndex;
22 minIndex = temp;
23 }
24
25 // Calculate minimum deletions using three strategies:
26 // Strategy 1: Delete from left side to cover both elements (maxIndex + 1)
27 // Strategy 2: Delete from right side to cover both elements (arrayLength - minIndex)
28 // Strategy 3: Delete from left to cover minIndex, and from right to cover maxIndex
29 // (minIndex + 1) + (arrayLength - maxIndex)
30 return Math.min(Math.min(maxIndex + 1, arrayLength - minIndex),
31 minIndex + 1 + arrayLength - maxIndex);
32 }
33}
34
1class Solution {
2public:
3 int minimumDeletions(vector<int>& nums) {
4 // Find indices of minimum and maximum elements
5 int minIndex = 0, maxIndex = 0;
6 int n = nums.size();
7
8 for (int i = 0; i < n; ++i) {
9 if (nums[i] < nums[minIndex]) {
10 minIndex = i;
11 }
12 if (nums[i] > nums[maxIndex]) {
13 maxIndex = i;
14 }
15 }
16
17 // Ensure minIndex is always less than or equal to maxIndex for easier calculation
18 if (minIndex > maxIndex) {
19 swap(minIndex, maxIndex);
20 }
21
22 // Calculate minimum deletions using three strategies:
23 // 1. Delete from left until both elements are removed (maxIndex + 1)
24 // 2. Delete from right until both elements are removed (n - minIndex)
25 // 3. Delete from left to remove one element and from right to remove the other
26 // (minIndex + 1) + (n - maxIndex)
27 int deleteFromLeft = maxIndex + 1;
28 int deleteFromRight = n - minIndex;
29 int deleteFromBothEnds = (minIndex + 1) + (n - maxIndex);
30
31 return min({deleteFromLeft, deleteFromRight, deleteFromBothEnds});
32 }
33};
34
1/**
2 * Finds the minimum number of deletions needed to remove both the minimum and maximum elements
3 * from an array, where deletions can only happen from either end of the array.
4 *
5 * @param nums - The input array of numbers
6 * @returns The minimum number of deletions required
7 */
8function minimumDeletions(nums: number[]): number {
9 const arrayLength = nums.length;
10
11 // Handle edge case: single element array
12 if (arrayLength === 1) {
13 return 1;
14 }
15
16 // Find indices of minimum and maximum elements
17 const minElementIndex = nums.indexOf(Math.min(...nums));
18 const maxElementIndex = nums.indexOf(Math.max(...nums));
19
20 // Determine which element appears first (leftmost) and last (rightmost)
21 const leftmostIndex = Math.min(minElementIndex, maxElementIndex);
22 const rightmostIndex = Math.max(minElementIndex, maxElementIndex);
23
24 // Calculate three possible strategies:
25 // Strategy 1: Delete from left to get one element, then from right to get the other
26 const deleteFromBothEnds = (leftmostIndex + 1) + (arrayLength - rightmostIndex);
27
28 // Strategy 2: Delete from left only to get both elements
29 const deleteFromLeftOnly = rightmostIndex + 1;
30
31 // Strategy 3: Delete from right only to get both elements
32 const deleteFromRightOnly = arrayLength - leftmostIndex;
33
34 // Return the minimum deletions among all strategies
35 return Math.min(deleteFromBothEnds, deleteFromLeftOnly, deleteFromRightOnly);
36}
37
Time and Space Complexity
Time Complexity: O(n)
- The code iterates through the array once using
enumerate(nums)
to find the indices of the minimum and maximum elements, which takesO(n)
time - The swap operation
mi, mx = mx, mi
takesO(1)
time - The final calculation with
min()
comparing three values takesO(1)
time - Overall time complexity is
O(n)
wheren
is the length of the input array
Space Complexity: O(1)
- The algorithm uses only a constant amount of extra space
- Variables
mi
,mx
,i
, andnum
are the only additional variables created - No additional data structures are used that scale with input size
- The space complexity is
O(1)
- constant space
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting to Handle Single Element Arrays
While the given solution handles single element arrays correctly (when n=1, all three strategies return 1), developers might overcomplicate this edge case or add unnecessary special handling.
Why it works: When the array has only one element, both min and max indices are 0, so:
- Strategy 1:
0 + 1 = 1
- Strategy 2:
1 - 0 = 1
- Strategy 3:
0 + 1 + 1 - 0 = 2
The minimum is 1, which is correct.
2. Incorrectly Calculating Deletions from the Back
A common mistake is using n - mi - 1
instead of n - mi
when calculating deletions from the back.
Incorrect:
return min(mx + 1, len(nums) - mi - 1, mi + 1 + len(nums) - mx - 1)
Correct:
return min(mx + 1, len(nums) - mi, mi + 1 + len(nums) - mx)
Why: To delete from the back until index i
is removed, we need to delete n - i
elements, not n - i - 1
.
3. Not Swapping Indices When Necessary
Some developers might assume the minimum element always appears before the maximum element in the array, leading to incorrect calculations.
Problem Example: In array [5, 1, 3, 9, 2]
, the minimum (1) is at index 1, and the maximum (9) is at index 3. Without swapping, the logic is straightforward.
But in array [5, 9, 3, 1, 2]
, the minimum (1) is at index 3, and the maximum (9) is at index 1. Without ensuring mi < mx
, the third strategy calculation becomes confusing.
Solution: Always normalize by swapping so that mi
represents the leftmost target and mx
represents the rightmost target:
if min_index > max_index: min_index, max_index = max_index, min_index
4. Overthinking When Min and Max Are the Same Element
In arrays where all elements are identical (though the problem states "distinct integers"), or conceptually when thinking about the algorithm, developers might add special cases.
Reality: The algorithm naturally handles this case. If min and max are at the same index, all three strategies will correctly calculate the deletions needed to remove that single element.
5. Using Wrong Variable Names Leading to Logic Errors
Using ambiguous names like left
and right
instead of min_index
and max_index
can cause confusion about what these variables represent, especially after swapping.
Better Practice: Use descriptive names that clearly indicate what the variable represents throughout the algorithm's execution.
Is the following code DFS or BFS?
void search(Node root) { if (!root) return; visit(root); root.visited = true; for (Node node in root.adjacent) { if (!node.visited) { search(node); } } }
Recommended Readings
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
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donβt Miss This!