Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions problems/2091-removing-minimum-and-maximum-from-array/analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# 2091. Removing Minimum and Maximum From Array

[LeetCode Link](https://leetcode.com/problems/removing-minimum-and-maximum-from-array/)

Difficulty: Medium
Topics: Array, Greedy
Acceptance Rate: 61.1%

## Hints

### Hint 1

The values in the array are mostly a distraction. Only two of them ever matter: the minimum and the maximum. Once you know *where* those two live, the rest of the array is just padding. Try restating the problem purely in terms of two indices in a length-`n` array.

### Hint 2

Deletions only happen at the two ends, so any sequence of deletions leaves behind a contiguous middle segment. That means the whole strategy is described by "how many did I take from the front" and "how many did I take from the back" — the *order* of the deletions never matters. Both target elements must end up outside the surviving middle segment.

### Hint 3

With two indices `lo <= hi`, there are exactly three ways to get both of them out, and you just take the cheapest:

1. Sweep from the front past both: `hi + 1` deletions.
2. Sweep from the back past both: `n - lo` deletions.
3. Meet in the middle — take `lo` from the front and `hi` from the back: `(lo + 1) + (n - hi)` deletions.

There is no fourth option, because a strategy that reaches `lo` from the back has already passed `hi`, and vice versa.

## Approach

The key reframing is that this is not really an array problem, it's a two-index geometry problem.

**Step 1 — locate the two targets.** Scan the array once, tracking the index of the smallest value and the index of the largest value. The problem guarantees distinct integers, so both are unambiguous. Let `i` be the min's index and `j` the max's index.

**Step 2 — normalize.** We don't care which one is the min and which is the max, only their positions. Set `lo = min(i, j)` and `hi = max(i, j)`. Let `n = len(nums)`.

**Step 3 — enumerate the three strategies.** Every deletion removes an element from the front or the back, so after any number of deletions the array that remains is a contiguous slice `nums[a : n-b]` where `a` is the front count and `b` is the back count, costing `a + b`. We need both `lo` and `hi` excluded from that slice. Since `lo <= hi`, exactly three minimal configurations exist:

- **Front only:** delete through index `hi`, which necessarily also removes `lo`. Cost `hi + 1`.
- **Back only:** delete back through index `lo`, which necessarily also removes `hi`. Cost `n - lo`.
- **Split:** remove `lo` from the front (cost `lo + 1`) and `hi` from the back (cost `n - hi`). Total `(lo + 1) + (n - hi)`.

Any other combination is dominated by one of these — e.g. deleting extra elements past `hi` from the front only adds cost.

**Step 4 —** return the minimum of the three.

**Worked example.** `nums = [2, 10, 7, 5, 4, 1, 8, 6]`, so `n = 8`. The minimum `1` sits at index 5, the maximum `10` at index 1, giving `lo = 1`, `hi = 5`.

- Front only: `5 + 1 = 6`
- Back only: `8 - 1 = 7`
- Split: `(1 + 1) + (8 - 5) = 2 + 3 = 5`

The answer is `5`, matching the expected output: two from the front (`2, 10`) and three from the back (`8, 6` plus `1`).

One nice property of the split formula is that it never *undercounts* when `lo == hi` (a single-element array). There it evaluates to `n + 1`, which loses to the other two options, so no special-casing is needed.

This is a Medium mostly because of the "three cases, and only three" reasoning — the code itself is a single pass and a couple of `min` calls. If you found yourself reaching for two pointers or a sliding window, that's a very common first instinct here; the trap is treating deletion as a process rather than as a choice of a surviving window.

## Complexity Analysis

Time Complexity: O(n) — one pass to find the two indices, then constant work.
Space Complexity: O(1) — only a handful of index variables.

## Edge Cases

- **Single element (`n == 1`):** the lone element is both the minimum and the maximum, so `lo == hi == 0` and the answer is `1`. The front-only and back-only formulas both yield `1`; the split formula yields `2` and is correctly discarded by the `min`.
- **Two elements:** whichever order they appear in, `lo = 0` and `hi = 1`, and all three formulas give `2`. You must delete both, so `2` is right.
- **Min and max at opposite ends:** e.g. `[1, 3, 4, 2, 5]`. The split strategy wins with `2`, while the sweep strategies would cost `n`. This is the case that makes the third formula necessary.
- **Min and max adjacent near one end:** e.g. `[0, -4, 19, 1, 8, -2, -3, 5]`. A single-sided sweep beats the split, which is why you can't just always meet in the middle.
- **Max appears before min:** the `lo`/`hi` normalization handles this; forgetting it and hardcoding `minIdx <= maxIdx` produces negative or inflated counts.
- **All negative values:** initializing the running min/max to `0` instead of `nums[0]` breaks on inputs like `[-5, -1, -3]`. Seed from the first element.
70 changes: 70 additions & 0 deletions problems/2091-removing-minimum-and-maximum-from-array/problem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
number: "2091"
frontend_id: "2091"
title: "Removing Minimum and Maximum From Array"
slug: "removing-minimum-and-maximum-from-array"
difficulty: "Medium"
topics:
- "Array"
- "Greedy"
acceptance_rate: 6105.5
is_premium: false
created_at: "2026-08-30T05:37:34.724282+00:00"
fetched_at: "2026-08-30T05:37:34.724282+00:00"
link: "https://leetcode.com/problems/removing-minimum-and-maximum-from-array/"
date: "2026-08-30"
---

# 2091. Removing Minimum and Maximum From Array

You are given a **0-indexed** array of **distinct** integers `nums`.

There is an element in `nums` that has the **lowest** value and an element that has the **highest** value. We call them the **minimum** and **maximum** respectively. Your goal is to remove **both** these elements from the array.

A **deletion** is defined as either removing an element from the **front** of the array or removing an element from the **back** of the array.

Return _the**minimum** number of deletions it would take to remove **both** the minimum and maximum element from the array._



**Example 1:**


**Input:** nums = [2,_**10**_ ,7,5,4,_**1**_ ,8,6]
**Output:** 5
**Explanation:**
The minimum element in the array is nums[5], which is 1.
The maximum element in the array is nums[1], which is 10.
We can remove both the minimum and maximum by removing 2 elements from the front and 3 elements from the back.
This results in 2 + 3 = 5 deletions, which is the minimum number possible.


**Example 2:**


**Input:** nums = [0,_**-4**_ ,_**19**_ ,1,8,-2,-3,5]
**Output:** 3
**Explanation:**
The minimum element in the array is nums[1], which is -4.
The maximum element in the array is nums[2], which is 19.
We can remove both the minimum and maximum by removing 3 elements from the front.
This results in only 3 deletions, which is the minimum number possible.


**Example 3:**


**Input:** nums = [_**101**_]
**Output:** 1
**Explanation:**
There is only one element in the array, which makes it both the minimum and maximum element.
We can remove it with 1 deletion.




**Constraints:**

* `1 <= nums.length <= 105`
* `-105 <= nums[i] <= 105`
* The integers in `nums` are **distinct**.
38 changes: 38 additions & 0 deletions problems/2091-removing-minimum-and-maximum-from-array/solution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package main

// Greedy, single pass.
//
// Deletions only happen at the two ends, so whatever survives is a contiguous
// middle slice nums[a : n-b] costing a+b. Locate the min and max indices and
// normalize them to lo <= hi; there are then exactly three ways to push both
// outside the surviving slice:
//
// front only: hi + 1
// back only: n - lo
// split: (lo + 1) + (n - hi)
//
// The answer is the cheapest of the three. Runs in O(n) time, O(1) space.
func minimumDeletions(nums []int) int {
n := len(nums)
if n == 0 {
return 0
}

minIdx, maxIdx := 0, 0
for i, v := range nums {
if v < nums[minIdx] {
minIdx = i
}
if v > nums[maxIdx] {
maxIdx = i
}
}

lo, hi := min(minIdx, maxIdx), max(minIdx, maxIdx)

fromFront := hi + 1
fromBack := n - lo
split := (lo + 1) + (n - hi)

return min(fromFront, min(fromBack, split))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package main

import "testing"

func TestSolution(t *testing.T) {
tests := []struct {
name string
nums []int
expected int
}{
{
name: "example 1: split, 2 from front and 3 from back",
nums: []int{2, 10, 7, 5, 4, 1, 8, 6},
expected: 5,
},
{
name: "example 2: min and max adjacent near the front",
nums: []int{0, -4, 19, 1, 8, -2, -3, 5},
expected: 3,
},
{
name: "example 3: single element is both min and max",
nums: []int{101},
expected: 1,
},
{
name: "edge case: two elements, must delete both",
nums: []int{1, 2},
expected: 2,
},
{
name: "edge case: min and max at opposite ends, split wins",
nums: []int{1, 3, 4, 2, 5},
expected: 2,
},
{
name: "edge case: strictly decreasing, max first and min last",
nums: []int{5, 4, 3, 2, 1},
expected: 2,
},
{
name: "edge case: min and max both in the middle, front sweep wins",
nums: []int{3, 1, 5, 2, 4},
expected: 3,
},
{
name: "edge case: all negative values",
nums: []int{-5, -1, -3},
expected: 2,
},
{
name: "edge case: max before min near the end, back sweep wins",
nums: []int{3, 4, 5, 6, 7, 9, 8, 2},
expected: 3,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := minimumDeletions(tt.nums)
if result != tt.expected {
t.Errorf("minimumDeletions(%v) = %v, want %v", tt.nums, result, tt.expected)
}
})
}
}