难度简单58
给你一个 下标从 0 开始 的整数数组 nums
,其中 nums[i]
表示第 i
名学生的分数。另给你一个整数 k
。
从数组中选出任意 k
名学生的分数,使这 k
个分数间 最高分 和 最低分 的 差值 达到 最小化 。
返回可能的 最小差值 。
示例 1:
输入:nums = [90], k = 1 输出:0 解释:选出 1 名学生的分数,仅有 1 种方法: - [90] 最高分和最低分之间的差值是 90 - 90 = 0 可能的最小差值是 0
示例 2:
输入:nums = [9,4,1,7], k = 2 输出:2 解释:选出 2 名学生的分数,有 6 种方法: - [9,4,1,7] 最高分和最低分之间的差值是 9 - 4 = 5 - [9,4,1,7] 最高分和最低分之间的差值是 9 - 1 = 8 - [9,4,1,7] 最高分和最低分之间的差值是 9 - 7 = 2 - [9,4,1,7] 最高分和最低分之间的差值是 4 - 1 = 3 - [9,4,1,7] 最高分和最低分之间的差值是 7 - 4 = 3 - [9,4,1,7] 最高分和最低分之间的差值是 7 - 1 = 6 可能的最小差值是 2
提示:
1 <= k <= nums.length <= 1000
0 <= nums[i] <= 105
思路:由于是任意取k个,那么很明显,我们将数组排序后,枚举长度为k的子区间,最终答案一定由这些产生,因为排序可以保证排序后在一个长度为k的连续区间内,头尾的差一定是最小的。
理解:假设我们取排序后数组的一个长度为k的子数组部分,现在我们考虑将其中一个值替换掉,那么由于排序后的数值是连续的,因此被替换的数只可能是这个区间的端值(即最大值或者最小值);我们不讨论和端值相等的情况,因为这种情况下替换和不替换带来的结果是一样的。
不妨设是最大值被替换了,那么替换后的新值一定比原来的值要大,此时我们可以选择把原区间内部的最小值丢弃,而把刚刚被替换的端值重新加入,我们看看发生了什么?
原区间:i,i+1,...,j
替换最大值:i,i+1,...,j+1
丢弃最小值,重新放入被替换的端值:i+1,...,j,j+1
可以发现,这就对应了在有序数组上把k窗口向后移动了一步
换个思路理解:排序后的数组,其中的数是按照顺序紧密靠在一起的,如果某个值发生了替换,在不考虑重复端值的情况下,一定会使得原本紧密的数中间拉开了距离,即导致最小值和最大值差值变大。
以上述数轴为例,假设原来要判断的区间为第一第二第三,现在将第三替换为第四,这必定会拉大最小值和最大值的差。
class Solution {
public:
int minimumDifference(vector<int>& nums, int k) {
sort(nums.begin(), nums.end());
int i, j, min_diff = nums[nums.size() - 1];
for(i = 0; i + k <= nums.size(); ++ i){
j = i + k - 1;
min_diff = min(min_diff, nums[j] - nums[i]);
}
return min_diff;
}
};
进阶:如果本题改成不能改变原数组顺序,不再是任意选取呢?很显然我们需要枚举出所有长度为k的子区间,然后求出区间内的最大值和最小值做差。对于这种问题,很显然可以用滑动窗口来解决,我们用一个单调递增队列来维护最小值,用一个单调递减队列来维护最大值,当前索引可以产生一个区间的时候,就从两个队列头取出元素并做差,然后更新答案。
class Solution {
public:
int minimumDifference(vector<int>& nums, int k) {
//
struct NumNode{
int index, num;
};
queue<NumNode> min_window, max_window;
int i, min_diff = 0x3f3f3f3f;
for(i = 0; i < nums.size(); ++ i){
while(!min_window.empty() && (i - min_window.front().index >= k || min_window.front().num >= nums[i])){
min_window.pop();
}
min_window.push(NumNode{i, nums[i]});
while(!max_window.empty() && (i - max_window.front().index >= k || max_window.front().num <= nums[i])){
max_window.pop();
}
max_window.push(NumNode{i, nums[i]});
if(i >= k - 1){
cout << max_window.front().num << " " << min_window.front().num << endl;
min_diff = min(min_diff, max_window.front().num - min_window.front().num);
}
}
return min_diff;
}
};