1984题"学生分数的最小差值"是一个典型的滑动窗口问题。题目要求从给定的分数数组中选出k个分数,使得这k个分数中的最大值和最小值的差值最小。这个问题的核心在于如何高效地找到这样的k个分数组合。
给定一个整数数组nums和一个整数k,我们需要从nums中选出k个元素,使得这k个元素中的最大值和最小值的差最小。返回这个最小的差值。
例如:
排序的必要性:为了找到最小差值,我们需要考虑相邻的元素,因为排序后相邻元素的差值会比不相邻的元素更小。这是解题的第一个关键点。
滑动窗口的应用:排序后,我们可以使用滑动窗口技术来检查所有可能的k个连续元素的组合,因为非连续的组合必然会产生更大的差值。
边界条件处理:当k=1时,差值为0;当数组长度为1时,直接返回0。
首先对数组进行排序,这是整个算法的基础。排序后我们可以确保:
cpp复制ranges::sort(nums); // C++20中的新语法,等同于sort(nums.begin(), nums.end());
注意:在实际面试中,如果环境不支持C++20,可以使用传统的sort函数。
排序后,我们使用一个大小为k的滑动窗口遍历数组,计算每个窗口的最大值和最小值的差,并记录最小的差值。
cpp复制for(int i=0; i+k-1 < n; i++) {
int diff = nums[i+k-1] - nums[i];
if(diff < ans) ans = diff;
}
这里的关键点:
在开始滑动窗口前,我们需要处理一些特殊情况:
cpp复制if(n == 1) return 0;
cpp复制class Solution {
public:
int minimumDifference(vector<int>& nums, int k) {
int n = nums.size(), ans = INT_MAX;
if(n == 1 || k == 1) return 0;
sort(nums.begin(), nums.end());
for(int i = 0; i + k - 1 < n; i++) {
int diff = nums[i + k - 1] - nums[i];
ans = min(ans, diff);
}
return ans;
}
};
排序是为了确保我们能够通过滑动窗口找到最小差值。如果不排序,我们需要检查所有可能的k个元素的组合,这将导致O(n^k)的时间复杂度,显然不可行。
因为题目要求选择k个元素,我们需要考虑所有连续的k个元素的组合。窗口大小小于k会遗漏元素,大于k会包含多余元素。
根据题目描述,k的取值范围是1 <= k <= nums.length,所以不需要特别处理。
这类问题在实际中有很多应用,例如:
在实际解决这个问题时,有几点经验值得分享:
画图辅助理解:将数组元素画在数轴上,可以直观地看到排序后滑动窗口的效果。
边界测试:一定要测试k=1和数组长度为1的情况,这些边界条件容易忽略但很重要。
语言特性利用:如C++20的ranges::sort可以使代码更简洁,但要注意兼容性。
变量命名:使用有意义的变量名如minDiff比ans更能表达意图。
提前终止优化:虽然不影响复杂度,但在实际中可以减少不必要的计算。
这个问题的关键在于认识到排序后滑动窗口的有效性。一旦理解了这一点,实现就相对直接了。在面试中,清晰地解释这个思路比直接写代码更重要。