1984. Minimum Difference Between Highest and Lowest of K Scores
Problem Description
You have an array nums
where each element nums[i]
represents the score of the i-th student. Given an integer k
, you need to select exactly k
students from this array.
Your goal is to choose these k
students in such a way that the difference between the highest score and the lowest score among the selected students is as small as possible.
For example, if you have scores [9, 4, 1, 7]
and k = 2
, you could:
- Select students with scores 9 and 4: difference = 9 - 4 = 5
- Select students with scores 9 and 1: difference = 9 - 1 = 8
- Select students with scores 9 and 7: difference = 9 - 7 = 2
- Select students with scores 4 and 1: difference = 4 - 1 = 3
- Select students with scores 4 and 7: difference = 7 - 4 = 3
- Select students with scores 1 and 7: difference = 7 - 1 = 6
The minimum possible difference would be 2 (by selecting scores 9 and 7).
The solution approach sorts the array first, then checks all possible consecutive windows of size k
. This works because after sorting, any k
consecutive elements will have the smallest possible range compared to selecting non-consecutive elements. The difference for each window is calculated as nums[i+k-1] - nums[i]
, and the minimum among all these differences is returned.
Intuition
The key insight is recognizing that to minimize the difference between the highest and lowest scores, we want the selected scores to be as close together as possible.
Imagine the scores scattered on a number line. If we pick k
scores randomly, they might be spread far apart. But if we could group them tightly together, the difference between max and min would be smaller.
This leads to an important realization: after sorting the array, the optimal selection must be a consecutive group of k
elements. Why? Consider any selection of k
elements from a sorted array. If these elements are not consecutive, there are gaps between them. We could replace the highest element with a lower one (filling in a gap) or replace the lowest element with a higher one (filling in a gap), and this would either decrease the difference or keep it the same - it would never increase it.
For example, if we have sorted scores [1, 3, 5, 7, 9]
and k = 3
:
- Selecting non-consecutive elements like
[1, 5, 9]
gives difference = 9 - 1 = 8 - But selecting consecutive elements like
[5, 7, 9]
gives difference = 9 - 5 = 4
Once we understand that we need consecutive elements from a sorted array, the problem becomes straightforward: we slide a window of size k
through the sorted array and find which window has the minimum difference between its last and first elements. This is simply nums[i+k-1] - nums[i]
for each valid starting position i
.
Learn more about Sorting and Sliding Window patterns.
Solution Approach
The implementation follows a straightforward two-step process:
Step 1: Sort the array
nums.sort()
We sort the array in ascending order. This arranges all scores from lowest to highest, enabling us to find consecutive groups of k
students with minimal score differences.
Step 2: Sliding Window to find minimum difference
return min(nums[i + k - 1] - nums[i] for i in range(len(nums) - k + 1))
After sorting, we use a sliding window technique:
- The window size is
k
(we need to select exactlyk
students) - For each valid starting position
i
, the window contains elements from indexi
toi + k - 1
- The difference for each window is calculated as
nums[i + k - 1] - nums[i]
(last element minus first element in the window) - We iterate through all possible windows:
i
ranges from0
tolen(nums) - k
The expression range(len(nums) - k + 1)
ensures we don't go out of bounds. If we have n
elements and need windows of size k
, the last valid starting position is at index n - k
.
For example, with nums = [9, 4, 1, 7]
and k = 2
:
- After sorting:
[1, 4, 7, 9]
- Windows:
- Window at i=0:
[1, 4]
, difference = 4 - 1 = 3 - Window at i=1:
[4, 7]
, difference = 7 - 4 = 3 - Window at i=2:
[7, 9]
, difference = 9 - 7 = 2
- Window at i=0:
- Minimum difference = 2
The time complexity is O(n log n)
due to sorting, and the space complexity is O(1)
if we don't count the space used by sorting.
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 to see how it works step by step.
Example: nums = [3, 9, 6, 1, 8]
and k = 3
Step 1: Sort the array
- Original array:
[3, 9, 6, 1, 8]
- After sorting:
[1, 3, 6, 8, 9]
Step 2: Check all consecutive windows of size k=3
We'll slide a window of size 3 through the sorted array and calculate the difference for each window:
Window 1: Starting at index 0
- Elements:
[1, 3, 6]
- Difference:
6 - 1 = 5
Window 2: Starting at index 1
- Elements:
[3, 6, 8]
- Difference:
8 - 3 = 5
Window 3: Starting at index 2
- Elements:
[6, 8, 9]
- Difference:
9 - 6 = 3
Step 3: Find the minimum difference
- All differences:
[5, 5, 3]
- Minimum difference:
3
Therefore, the optimal selection is [6, 8, 9]
with a minimum difference of 3
.
Why this works:
Notice that if we tried to select non-consecutive elements from the sorted array, like [1, 6, 9]
, the difference would be 9 - 1 = 8
, which is larger than our answer. Any selection that "skips" elements in the sorted array will have a larger range than a consecutive selection that includes those same boundary elements or elements closer together.
Solution Implementation
1class Solution:
2 def minimumDifference(self, nums: List[int], k: int) -> int:
3 # Sort the array in ascending order to group similar values together
4 nums.sort()
5
6 # Find the minimum difference between max and min in any window of size k
7 # We iterate through all possible windows of size k
8 # For each window starting at index i, the difference is nums[i+k-1] - nums[i]
9 return min(nums[i + k - 1] - nums[i] for i in range(len(nums) - k + 1))
10
1class Solution {
2 public int minimumDifference(int[] nums, int k) {
3 // Sort the array in ascending order to group similar values together
4 Arrays.sort(nums);
5
6 // Initialize minimum difference with a large value (maximum possible difference)
7 int minDifference = 100000;
8
9 // Iterate through all possible windows of size k
10 // We stop at nums.length - k + 1 to ensure we have k elements in each window
11 for (int i = 0; i <= nums.length - k; i++) {
12 // Calculate the difference between the maximum (last) and minimum (first)
13 // elements in the current window of size k
14 int currentDifference = nums[i + k - 1] - nums[i];
15
16 // Update the minimum difference if current window has smaller difference
17 minDifference = Math.min(minDifference, currentDifference);
18 }
19
20 // Return the minimum difference found among all windows
21 return minDifference;
22 }
23}
24
1class Solution {
2public:
3 int minimumDifference(vector<int>& nums, int k) {
4 // Sort the array in ascending order to group similar values together
5 sort(nums.begin(), nums.end());
6
7 // Initialize the minimum difference with a large value (maximum possible)
8 int minDiff = 100000;
9
10 // Iterate through all possible windows of size k
11 // We need to check windows starting from index 0 to index (n - k)
12 for (int i = 0; i <= nums.size() - k; ++i) {
13 // Calculate the difference between the last and first element in the current window
14 // Window spans from index i to index (i + k - 1)
15 int currentDiff = nums[i + k - 1] - nums[i];
16
17 // Update the minimum difference if current window has a smaller difference
18 minDiff = min(minDiff, currentDiff);
19 }
20
21 // Return the minimum difference found among all windows
22 return minDiff;
23 }
24};
25
1/**
2 * Finds the minimum difference between the maximum and minimum values
3 * in any subsequence of k consecutive elements from a sorted array
4 * @param nums - The input array of numbers
5 * @param k - The size of the subsequence window
6 * @returns The minimum difference found
7 */
8function minimumDifference(nums: number[], k: number): number {
9 // Sort the array in ascending order
10 nums.sort((a: number, b: number) => a - b);
11
12 // Get the length of the array
13 const arrayLength: number = nums.length;
14
15 // Initialize the answer with the maximum possible difference (last - first element)
16 let minDifference: number = nums[arrayLength - 1] - nums[0];
17
18 // Iterate through all possible windows of size k
19 // For each window, calculate the difference between max (last) and min (first) elements
20 for (let startIndex: number = 0; startIndex + k - 1 < arrayLength; startIndex++) {
21 const endIndex: number = startIndex + k - 1;
22 const currentDifference: number = nums[endIndex] - nums[startIndex];
23 minDifference = Math.min(currentDifference, minDifference);
24 }
25
26 return minDifference;
27}
28
Time and Space Complexity
The time complexity is O(n × log n)
, where n
is the length of the input array nums
. This is dominated by the sorting operation nums.sort()
, which uses Timsort algorithm with O(n × log n)
worst-case time complexity. The subsequent iteration through the array to find the minimum difference takes O(n - k + 1)
time, which simplifies to O(n)
, but this is overshadowed by the sorting step.
The space complexity is O(log n)
. While the sorting is performed in-place and doesn't require additional space proportional to the input size, the sorting algorithm (Timsort in Python) uses O(log n)
space for its recursive call stack during the merge operations. The generator expression min(nums[i + k - 1] - nums[i] for i in range(len(nums) - k + 1))
computes values on-the-fly without storing them, contributing O(1)
additional space.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Edge Case: k equals array length
One common pitfall is not handling the case where k
equals the length of the array. When k = len(nums)
, we must select all students, so there's only one possible selection.
Problem Code:
# This might cause issues if not careful with range calculation
return min(nums[i + k - 1] - nums[i] for i in range(len(nums) - k + 1))
Why it's problematic: When k = len(nums)
, the range becomes range(1)
, which only gives i = 0
. The generator expression works but might not be immediately obvious that it handles this edge case correctly.
Solution:
def minimumDifference(self, nums: List[int], k: int) -> int:
# Handle edge case explicitly for clarity
if k == len(nums):
return max(nums) - min(nums)
nums.sort()
return min(nums[i + k - 1] - nums[i] for i in range(len(nums) - k + 1))
2. Single Element Selection (k = 1)
Another pitfall is when k = 1
. Selecting only one student means there's no "difference" between highest and lowest scores since they're the same student.
Problem: The algorithm would return nums[0] - nums[0] = 0
for all windows, which is correct but might not match problem expectations.
Solution:
def minimumDifference(self, nums: List[int], k: int) -> int:
# Special case: selecting only one student
if k == 1:
return 0
nums.sort()
return min(nums[i + k - 1] - nums[i] for i in range(len(nums) - k + 1))
3. Empty Generator with Invalid k
If k > len(nums)
, the range calculation range(len(nums) - k + 1)
produces an empty range when the argument is negative or zero, causing the min()
function to fail.
Error Example:
# If nums has 3 elements and k = 5 # range(3 - 5 + 1) = range(-1) = empty range # min() on empty sequence raises ValueError
Solution:
def minimumDifference(self, nums: List[int], k: int) -> int:
# Validate input
if k > len(nums):
raise ValueError("Cannot select more students than available")
if k <= 0:
raise ValueError("Must select at least one student")
if k == 1:
return 0
nums.sort()
return min(nums[i + k - 1] - nums[i] for i in range(len(nums) - k + 1))
4. Modifying Original Array
The sort()
method modifies the original array in-place, which might not be desired if the original order needs to be preserved elsewhere.
Solution:
def minimumDifference(self, nums: List[int], k: int) -> int:
# Create a sorted copy to preserve original array
sorted_nums = sorted(nums)
return min(sorted_nums[i + k - 1] - sorted_nums[i]
for i in range(len(sorted_nums) - k + 1))
Which type of traversal does breadth first search do?
Recommended Readings
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
https assets algo monster cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in the window The window
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
Want a Structured Path to Master System Design Too? Don’t Miss This!