A clear explanation of minimizing an array score after each value can move by at most k.
Problem Restatement
We are given an integer array nums and an integer k.
For each index, we may change nums[i] by adding some integer x, where:
-k <= x <= kEach index can be changed at most once.
After the changes, the score of the array is:
max(nums) - min(nums)Return the minimum possible score. The official examples include [0, 10] with k = 2, where the answer is 6.
Input and Output
| Item | Meaning |
|---|---|
| Input | Integer array nums and integer k |
| Output | Minimum possible score |
| Operation | Add any integer from [-k, k] to each element |
| Score | Maximum value minus minimum value after changes |
Function shape:
def smallestRangeI(nums: list[int], k: int) -> int:
...Examples
Example 1:
nums = [1]
k = 0There is only one value, so the score is:
1 - 1 = 0Answer:
0Example 2:
nums = [0, 10]
k = 2We can change the array to:
[2, 8]The score is:
8 - 2 = 6Answer:
6Example 3:
nums = [1, 3, 6]
k = 3We can make the values overlap around the same point.
One valid result is:
[4, 4, 4]The score is:
0Answer:
0First Thought: Try All Adjustments
A direct approach is to try every possible change for every number.
For each nums[i], possible new values range from:
nums[i] - kto:
nums[i] + kThen we could test every combination and choose the smallest score.
This is not practical. The number of combinations grows too quickly.
We need to reason about the range directly.
Key Insight
Only the smallest and largest original values matter.
Let:
mn = min(nums)
mx = max(nums)The original score is:
mx - mnTo minimize the score:
- Increase the smallest value as much as possible.
- Decrease the largest value as much as possible.
So the smallest value can become at most:
mn + kThe largest value can become at least:
mx - kThe new gap is:
(mx - k) - (mn + k)which simplifies to:
mx - mn - 2 * kBut a score cannot be negative.
So the answer is:
max(0, mx - mn - 2 * k)This is the standard mathematical solution for this problem.
Algorithm
- Find the minimum value in
nums. - Find the maximum value in
nums. - Compute the original range:
mx - mn- Reduce it by
2 * k. - If the result is negative, return
0.
Correctness
Let mn be the minimum value and mx be the maximum value in the original array.
No value can increase by more than k, so the original minimum cannot be forced higher than mn + k.
No value can decrease by more than k, so the original maximum cannot be forced lower than mx - k.
Therefore, if the two adjusted extremes remain separated, the score cannot be smaller than:
(mx - k) - (mn + k)The algorithm achieves this bound by increasing the minimum-side values and decreasing the maximum-side values as needed.
If the intervals overlap, then the values can be moved to a common overlapping range, so the score can be 0.
Since scores cannot be negative, the minimum possible score is:
max(0, mx - mn - 2 * k)Therefore, the algorithm returns the optimal answer.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | We scan the array to find min and max |
| Space | O(1) | Only a few variables are used |
Implementation
class Solution:
def smallestRangeI(self, nums: list[int], k: int) -> int:
mn = min(nums)
mx = max(nums)
return max(0, mx - mn - 2 * k)Code Explanation
Find the smallest and largest values:
mn = min(nums)
mx = max(nums)The original range is:
mx - mnEach side can move inward by at most k, so the range can shrink by at most:
2 * kReturn the non-negative result:
return max(0, mx - mn - 2 * k)Testing
def run_tests():
s = Solution()
assert s.smallestRangeI([1], 0) == 0
assert s.smallestRangeI([0, 10], 2) == 6
assert s.smallestRangeI([1, 3, 6], 3) == 0
assert s.smallestRangeI([4, 8, 10], 1) == 4
assert s.smallestRangeI([5, 5, 5], 10) == 0
assert s.smallestRangeI([1, 100], 10) == 79
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
[1], k = 0 | Single value has score 0 |
[0, 10], k = 2 | Main sample with nonzero answer |
[1, 3, 6], k = 3 | Values can overlap to score 0 |
[4, 8, 10], k = 1 | Range shrinks but stays positive |
[5, 5, 5], k = 10 | Already zero range |
[1, 100], k = 10 | Large gap remains after shrinking |