Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Move from brute-force thinking to an efficient approach using array strategy.
You are given an integer array arr. You can choose a set of integers and remove all the occurrences of these integers in the array.
Return the minimum size of the set so that at least half of the integers of the array are removed.
Example 1:
Input: arr = [3,3,3,3,5,5,5,2,2,7]
Output: 2
Explanation: Choosing {3,7} will make the new array [5,5,5,2,2] which has size 5 (i.e equal to half of the size of the old array).
Possible sets of size 2 are {3,5},{3,2},{5,2}.
Choosing set {2,7} is not possible as it will make the new array [3,3,3,3,5,5,5] which has a size greater than half of the size of the old array.
Example 2:
Input: arr = [7,7,7,7,7,7]
Output: 1
Explanation: The only possible set you can choose is {7}. This will make the new array empty.
Constraints:
2 <= arr.length <= 105arr.length is even.1 <= arr[i] <= 105Problem summary: You are given an integer array arr. You can choose a set of integers and remove all the occurrences of these integers in the array. Return the minimum size of the set so that at least half of the integers of the array are removed.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map · Greedy
[3,3,3,3,5,5,5,2,2,7]
[7,7,7,7,7,7]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1338: Reduce Array Size to The Half
class Solution {
public int minSetSize(int[] arr) {
int mx = 0;
for (int x : arr) {
mx = Math.max(mx, x);
}
int[] cnt = new int[mx + 1];
for (int x : arr) {
++cnt[x];
}
Arrays.sort(cnt);
int ans = 0;
int m = 0;
for (int i = mx;; --i) {
if (cnt[i] > 0) {
m += cnt[i];
++ans;
if (m * 2 >= arr.length) {
return ans;
}
}
}
}
}
// Accepted solution for LeetCode #1338: Reduce Array Size to The Half
func minSetSize(arr []int) (ans int) {
mx := slices.Max(arr)
cnt := make([]int, mx+1)
for _, x := range arr {
cnt[x]++
}
sort.Ints(cnt)
for i, m := mx, 0; ; i-- {
if cnt[i] > 0 {
m += cnt[i]
ans++
if m >= len(arr)/2 {
return
}
}
}
}
# Accepted solution for LeetCode #1338: Reduce Array Size to The Half
class Solution:
def minSetSize(self, arr: List[int]) -> int:
cnt = Counter(arr)
ans = m = 0
for _, v in cnt.most_common():
m += v
ans += 1
if m * 2 >= len(arr):
break
return ans
// Accepted solution for LeetCode #1338: Reduce Array Size to The Half
struct Solution;
use std::collections::BinaryHeap;
use std::collections::HashMap;
impl Solution {
fn min_set_size(arr: Vec<i32>) -> i32 {
let n = arr.len();
let mut hm: HashMap<i32, usize> = HashMap::new();
for x in arr {
*hm.entry(x).or_default() += 1;
}
let mut pq: BinaryHeap<usize> = BinaryHeap::new();
for (_, v) in hm {
pq.push(v);
}
let mut sum = 0;
let mut res = 0;
while sum * 2 < n {
sum += pq.pop().unwrap();
res += 1;
}
res
}
}
#[test]
fn test() {
let arr = vec![3, 3, 3, 3, 5, 5, 5, 2, 2, 7];
let res = 2;
assert_eq!(Solution::min_set_size(arr), res);
let arr = vec![7, 7, 7, 7, 7, 7];
let res = 1;
assert_eq!(Solution::min_set_size(arr), res);
let arr = vec![1, 9];
let res = 1;
assert_eq!(Solution::min_set_size(arr), res);
let arr = vec![1000, 1000, 3, 7];
let res = 1;
assert_eq!(Solution::min_set_size(arr), res);
let arr = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let res = 5;
assert_eq!(Solution::min_set_size(arr), res);
}
// Accepted solution for LeetCode #1338: Reduce Array Size to The Half
function minSetSize(arr: number[]): number {
const cnt = new Map<number, number>();
for (const v of arr) {
cnt.set(v, (cnt.get(v) ?? 0) + 1);
}
let [ans, m] = [0, 0];
for (const v of Array.from(cnt.values()).sort((a, b) => b - a)) {
m += v;
++ans;
if (m * 2 >= arr.length) {
break;
}
}
return ans;
}
Use this to step through a reusable interview workflow for this problem.
Try every possible combination of choices. With n items each having two states (include/exclude), the search space is 2ⁿ. Evaluating each combination takes O(n), giving O(n × 2ⁿ). The recursion stack or subset storage uses O(n) space.
Greedy algorithms typically sort the input (O(n log n)) then make a single pass (O(n)). The sort dominates. If the input is already sorted or the greedy choice can be computed without sorting, time drops to O(n). Proving greedy correctness (exchange argument) is harder than the implementation.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Wrong move: Locally optimal choices may fail globally.
Usually fails on: Counterexamples appear on crafted input orderings.
Fix: Verify with exchange argument or monotonic objective before committing.