LeetCode 1984 - Minimum Difference Between Highest and Lowest of K Scores
The problem asks us to find the minimum possible difference between the highest and lowest scores when selecting exactly k scores from an array of student scores.
Difficulty: 🟢 Easy
Topics: Array, Sliding Window, Sorting
Solution
Problem Understanding
The problem asks us to find the minimum possible difference between the highest and lowest scores when selecting exactly k scores from an array of student scores. The input is a 0-indexed integer array nums where each element represents a student's score, and an integer k which is the number of scores to pick. The expected output is a single integer representing the minimum difference achievable between the highest and lowest scores among any k selected scores.
The constraints indicate that nums can have up to 1000 elements and each score can go up to 100,000. This rules out brute-force approaches that attempt to check every combination of k scores since the number of combinations grows exponentially. We also know that k is at least 1, so there will always be a valid subset. Important edge cases include k = 1 (difference is always 0) and arrays that are already sorted or contain repeated numbers.
The key insight here is that the problem is fundamentally about minimizing the spread of k consecutive scores when the array is sorted, which allows us to transform it into a sliding window problem after sorting.
Approaches
Brute Force
A brute-force approach would generate all possible subsets of size k from nums, calculate the difference between the maximum and minimum of each subset, and return the smallest difference found. While correct, this method is extremely inefficient because the number of subsets is combinatorial, specifically $C(n, k)$, which is infeasible for n = 1000.
Optimal Approach
The optimal approach leverages sorting. Once the array is sorted, any group of k consecutive elements will always include the smallest difference for that segment. By sliding a window of length k over the sorted array and computing the difference between the first and last elements of the window, we can efficiently find the minimum difference. Sorting ensures that the largest and smallest elements of each window are at the ends, eliminating the need to compute max and min repeatedly for every combination.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n choose k * k) | O(k) | Generate all subsets of size k and calculate their differences |
| Optimal | O(n log n) | O(1) | Sort the array and use a sliding window to find the minimal difference |
Algorithm Walkthrough
- Sort the array
numsin ascending order. Sorting ensures that elements close to each other have minimal differences, which is key to finding the smallest possible range ofkscores. - Initialize a variable
min_diffto a large number, e.g., infinity. This will keep track of the minimum difference encountered. - Iterate with a sliding window of size
kfrom the start of the array to the end. For each window starting at indexi, calculate the difference between the last and first elements of the window:nums[i + k - 1] - nums[i]. - Update
min_diffif the current window's difference is smaller than the previously recorded minimum. - Return
min_diffafter the loop finishes.
Why it works: Sorting ensures that for any window of length k, the maximum difference occurs between the first and last elements. Sliding through all possible windows guarantees that we examine every possible subset of k consecutive scores. Thus, the minimum difference found is the global minimum for all possible subsets.
Python Solution
from typing import List
class Solution:
def minimumDifference(self, nums: List[int], k: int) -> int:
if k == 1:
return 0
nums.sort()
min_diff = float('inf')
for i in range(len(nums) - k + 1):
diff = nums[i + k - 1] - nums[i]
min_diff = min(min_diff, diff)
return min_diff
In this Python implementation, we first handle the edge case k = 1 directly. Sorting ensures that the smallest differences between k scores are always found in consecutive subarrays. The loop iterates over all possible windows of length k, computing the difference and updating the minimum. Using float('inf') ensures we start with a value larger than any possible difference.
Go Solution
import "sort"
func minimumDifference(nums []int, k int) int {
if k == 1 {
return 0
}
sort.Ints(nums)
minDiff := int(^uint(0) >> 1) // initialize to max int
for i := 0; i <= len(nums) - k; i++ {
diff := nums[i+k-1] - nums[i]
if diff < minDiff {
minDiff = diff
}
}
return minDiff
}
In Go, we handle k = 1 similarly. We use sort.Ints to sort the array, and int(^uint(0) >> 1) to initialize the maximum integer value. The sliding window logic is identical to Python, iterating over each consecutive k-length window and updating the minimum difference.
Worked Examples
Example 1: nums = [90], k = 1
| Step | Window | Difference | min_diff |
|---|---|---|---|
| Initial | [90] | 90-90=0 | inf → 0 |
Return 0.
Example 2: nums = [9,4,1,7], k = 2
Sorted array: [1, 4, 7, 9]
| i | Window | Difference | min_diff |
|---|---|---|---|
| 0 | [1,4] | 4-1=3 | 3 |
| 1 | [4,7] | 7-4=3 | 3 |
| 2 | [7,9] | 9-7=2 | 2 |
Return 2.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n log n) | Sorting dominates; sliding window is O(n) |
| Space | O(1) | Sorting in-place, no extra significant space |
Sorting is the main cost, and the sliding window pass is linear, making the algorithm very efficient.
Test Cases
# Provided examples
assert Solution().minimumDifference([90], 1) == 0
assert Solution().minimumDifference([9,4,1,7], 2) == 2
# Edge cases
assert Solution().minimumDifference([1,2,3,4,5], 5) == 4 # full array
assert Solution().minimumDifference([1,1,1,1], 2) == 0 # identical elements
assert Solution().minimumDifference([10,100,300,200], 2) == 90 # non-consecutive numbers
# Large k and unordered
assert Solution().minimumDifference([100,1,50,20,30], 3) == 20
| Test | Why |
|---|---|
| [90], 1 | k=1 edge case, difference is always 0 |
| [9,4,1,7], 2 | basic case from prompt |
| [1,2,3,4,5], 5 | selecting entire array |
| [1,1,1,1], 2 | identical elements should return 0 |
| [10,100,300,200], 2 | checks unordered array with gaps |
| [100,1,50,20,30], 3 | arbitrary array, medium k |
Edge Cases
- Single element (
k=1): The difference is always 0. Our implementation handles this directly with an early return. - All elements identical: Any subset will also have a difference of 0. Sorting and sliding window logic still works correctly because the difference computation yields 0.
kequals array length: The only valid subset is the entire array. Sorting ensures we correctly compute the difference between the largest and smallest elements. Our sliding window loop also handles this case naturally withlen(nums) - k + 1 = 1iteration.
This approach and implementation robustly handle the full spectrum of possible inputs while remaining efficient.