题目描述:
给定一个排序好的数组,两个整数 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
方法1:二分查找+双指针
主要思路:
(1)先使用二分查找找出最接近数值 x 的位置;
(2)再以该位置为中心,向左右方向扩展范围,知道数量满足要求 k;
class Solution {
public:
vector<int> findClosestElements(vector<int>& arr, int k, int x) {
//处理两种特殊的情形
if(x>=arr.back()){
return vector<int>(arr.end()-k,arr.end());
}
if(x<=arr[0]){
return vector<int>(arr.begin(),arr.begin()+k);
}
//确定初始的搜索范围
int left=0;
int right=arr.size()-1;
int mid=0;
//若范围到剩余一个元素,既left==right的位置
while(left<right){
mid=left+(right-left)/2;
if(arr[mid]==x){
left=mid;
right=mid;
}
else if(arr[mid]<x){
left=mid+1;
}
else{
right=mid-1;
}
}
//以找到的位置为中心,向前后两个位置进行判断,是否需要更新当前的中心位置
mid=right;
if(right<arr.size()-1&&abs(arr[mid]-x)>abs(arr[right+1]-x))
mid=right+1;
if(left>0&&abs(arr[mid]-x)>=abs(arr[left-1]-x))
mid=left-1;
//以当前位置为中心,想前后扩展范围,知道包含k个元素
left=mid-1;
right=mid+1;
//终止条件是范围内有k个元素
while(--k){
//处理左边界越界情形
if(left<0)
++right;
else if(right==arr.size())//处理右边界越界情形
--left;
else if(abs(arr[left]-x)<=abs(arr[right]-x))//根据左右边界指向的值和x 的远近,扩展边界
--left;
else
++right;
}
return vector<int>(arr.begin()+left+1,arr.begin()+right);//返回边界内的值
}
};