diff --git a/problems/2091-removing-minimum-and-maximum-from-array/analysis.md b/problems/2091-removing-minimum-and-maximum-from-array/analysis.md new file mode 100644 index 0000000..738ab12 --- /dev/null +++ b/problems/2091-removing-minimum-and-maximum-from-array/analysis.md @@ -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. diff --git a/problems/2091-removing-minimum-and-maximum-from-array/problem.md b/problems/2091-removing-minimum-and-maximum-from-array/problem.md new file mode 100644 index 0000000..1a48510 --- /dev/null +++ b/problems/2091-removing-minimum-and-maximum-from-array/problem.md @@ -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**. diff --git a/problems/2091-removing-minimum-and-maximum-from-array/solution.go b/problems/2091-removing-minimum-and-maximum-from-array/solution.go new file mode 100644 index 0000000..a6c035b --- /dev/null +++ b/problems/2091-removing-minimum-and-maximum-from-array/solution.go @@ -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)) +} diff --git a/problems/2091-removing-minimum-and-maximum-from-array/solution_test.go b/problems/2091-removing-minimum-and-maximum-from-array/solution_test.go new file mode 100644 index 0000000..cbd572f --- /dev/null +++ b/problems/2091-removing-minimum-and-maximum-from-array/solution_test.go @@ -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) + } + }) + } +}