Problem: 1984. 学生分数的最小差值
文章目录
- 整体思路
-
-
- [1. 核心问题](#1. 核心问题)
- [2. 算法逻辑:排序 + 滑动窗口](#2. 算法逻辑:排序 + 滑动窗口)
-
- 完整代码
- 时空复杂度
-
-
- [1. 时间复杂度: O ( N log N ) O(N \log N) O(NlogN)](#1. 时间复杂度: O ( N log N ) O(N \log N) O(NlogN))
- [2. 空间复杂度: O ( log N ) O(\log N) O(logN)](#2. 空间复杂度: O ( log N ) O(\log N) O(logN))
-
整体思路
1. 核心问题
我们需要从数组 nums 中选出 k 个元素,使得这 k 个元素中最大值 与最小值的差(即极差)最小。
2. 算法逻辑:排序 + 滑动窗口
-
为什么需要排序?
- 如果数组是乱序的,任意选
k个数,我们很难直观判断哪组数的差值最小。 - 但是,如果我们把数组从小到大排序,那么数值相近的元素就会挨在一起。
- 在排序后的数组中,为了让
max - min最小,选取的k个数必然是连续 的。- 例如:在
[1, 4, 7, 9]中选 3 个数。选[1, 4, 7]优于选[1, 7, 9],因为后者跨度更大。
- 例如:在
- 因此,问题转化为:在排序后的数组中,找到一个长度为
k的连续子数组(滑动窗口),使其末尾元素(该窗口最大值)与开头元素(该窗口最小值)的差值最小。
- 如果数组是乱序的,任意选
-
具体步骤:
- 排序 :使用
Arrays.sort(nums)对数组进行升序排列。 - 滑动窗口遍历 :
- 窗口大小固定为
k。 - 窗口的左边界为
i,右边界为i + k - 1。 - 在该窗口内,最大值是
nums[right],最小值是nums[left]。 - 计算差值
diff = nums[i + k - 1] - nums[i]。 - 遍历所有可能的窗口,维护全局最小的
diff。
- 窗口大小固定为
- 排序 :使用
完整代码
java
import java.util.Arrays;
class Solution {
public int minimumDifference(int[] nums, int k) {
// 1. 排序
// 将数组按从小到大排序,这是使用滑动窗口策略的前提
Arrays.sort(nums);
// 初始化结果为整型最大值,用于后续找最小值
int ans = Integer.MAX_VALUE;
// 2. 滑动窗口遍历
// i 代表当前窗口的起始位置(最小值位置)
// i + k - 1 代表当前窗口的结束位置(最大值位置)
// 循环条件 i < nums.length - k + 1 保证窗口右边界不越界
for (int i = 0; i < nums.length - k + 1; i++) {
// 计算当前窗口的极差:最大值 - 最小值
// 由于已排序,最大值一定是窗口最右侧,最小值是窗口最左侧
int currentDiff = nums[i + k - 1] - nums[i];
// 更新全局最小差值
ans = Math.min(ans, currentDiff);
}
return ans;
}
}
时空复杂度
假设数组 nums 的长度为 N N N。
1. 时间复杂度: O ( N log N ) O(N \log N) O(NlogN)
- 排序 :
Arrays.sort使用双轴快速排序(对于基本类型),时间复杂度为 O ( N log N ) O(N \log N) O(NlogN)。这是算法中耗时最主要的部分。 - 遍历 :滑动窗口遍历只需一次线性扫描,时间复杂度为 O ( N ) O(N) O(N)(具体循环次数为 N − k + 1 N - k + 1 N−k+1)。
- 总计 : O ( N log N ) + O ( N ) = O ( N log N ) O(N \log N) + O(N) = O(N \log N) O(NlogN)+O(N)=O(NlogN)。
2. 空间复杂度: O ( log N ) O(\log N) O(logN)
- 计算依据 :
- 代码只使用了常数个额外变量 (
ans,i)。 - 但是,Java 的
Arrays.sort对于基本数据类型int[]是原地排序,其内部递归调用栈需要消耗 O ( log N ) O(\log N) O(logN) 的空间。
- 代码只使用了常数个额外变量 (
- 结论 : O ( log N ) O(\log N) O(logN)。