问题描述:
给定一个排序好的数组,两个整数 k
和 x
,从数组中找到最靠近 x
(两数之差最小)的 k
个数。返回的结果必须要是按升序排好的。如果有两个数与 x
的差值一样,优先选择数值较小的那个数。
示例 1:
输入: [1,2,3,4,5], k=4, x=3 输出: [1,2,3,4]
示例 2:
输入: [1,2,3,4,5], k=4, x=-1 输出: [1,2,3,4]
说明:
- k 的值为正数,且总是小于给定排序数组的长度。
- 数组不为空,且长度不超过 104
- 数组里的每个元素与 x 的绝对值不超过 104
基本思路:
利用二分找到最近的元素,左右指针确定区间,最后遍历这个区间把结果加到结果集中。
AC代码:
class Solution {
public:
vector<int> findClosestElements(vector<int>& arr, int k, int x) {
if(arr.size() == 1){
return arr;
}
int beg = 0;
int end = arr.size() - k;
while(beg < end){
int mid = beg + (end - beg)/2;
if(abs(arr[mid] - x) > abs(arr[mid + k] -x)){
beg = mid + 1;
}
else{
end = mid;
}
}
return vector<int>{arr.begin()+beg, arr.begin()+beg+k};
}
};
大牛的思路:
由于是k个固定的元素,这k个元素必然连在一起。
所以我们想到了滑动窗口。
窗口最左边和最右边的元素是符合要求的最后两个元素,我们移动窗口,只需要判断边缘位置就行了。
这里还用到了二分的思想,仔细想想其实是正确的。
class Solution {
public:
vector<int> findClosestElements(vector<int>& arr, int k, int x) {
if(arr.size() == 1){
return arr;
}
int beg = 0;
int end = arr.size() - k;
while(beg < end){
int mid = beg + (end - beg)/2;
if(abs(arr[mid] - x) > abs(arr[mid + k] -x)){
beg = mid + 1;
}
else{
end = mid;
}
}
return vector<int>{arr.begin()+beg, arr.begin()+beg+k};
}
};
其他经验:
- 使用二分模板3的时候一定要注意mid停在left或者right的情况,想办法解决它。(现在我没有一个比较好的方法,看结果,如果它最终停在left上,修改mid;如果停在right上,满足要求时right = mid - 1)
- 有时候我们会将一个区间作为一个单位来二分,其实这就是二分+滑动窗口。