Given a sorted array, two integers k
and x
, find the k
closest elements to x
in the array. The result should also be sorted in ascending order. If there is a tie, the smaller elements are always preferred.
Example 1:
Input: [1,2,3,4,5], k=4, x=3 Output: [1,2,3,4]
Example 2:
Input: [1,2,3,4,5], k=4, x=-1 Output: [1,2,3,4]
Note:
- The value k is positive and will always be smaller than the length of the sorted array.
- Length of the given array is positive and will not exceed 104
- Absolute value of elements in the array and x will not exceed 104
1.解析
题目大意,求解跟x差值最小的k个元素,所给的序列是递增的。
2.分析
题目整体上看,还是比较简单的。下面是我所想到的解法,核心思想:利用stl库的lower_bound函数查找序列的第一个大于或等于x的元素的位置,然后以该位置作为起点,向左右两边寻找。但要注意的一个问题是,左边当到达begin的位置时,要单独标记一下,因为迭代器begin往前面的一个位置时未知的,不想end迭代器。所以我利用一个flag布尔变量标记左边到达begin位置。
class Solution {
public:
vector<int> findClosestElements(vector<int>& arr, int k, int x) {
auto it = lower_bound(arr.begin(), arr.end(), x); //查找第一个大于或等于x的值
if (it == arr.end()) return vector<int>{arr.end() - k, arr.end()};
else if (it == arr.begin()) return vector<int>{arr.begin(), arr.begin() + k};
vector<int> res;
bool flag = false;
auto cur = it;
cur--;
while (k-- > 0){
if (it == arr.end()){ //右边到达边界,则只需依次取左边的元素
res.push_back(*cur); --cur;
continue;
}
else if (flag) { //左边到达边界,则只需依次取右边的元素
res.push_back(*it); ++it;
continue;
}
int next_diff = *it - x;
int pre_diff = x - *cur;
if (pre_diff > next_diff){ //若右边的元素与x的差值小,则取右边的元素,并将指针往右移一个位置
res.push_back(*it);
++it;
}
else{ //若左边的元素与x的差值小,则取左边的元素,并将指针往左移一个位置
res.push_back(*cur);
if (cur == arr.begin()) flag = true; //到达首部
else --cur;
}
}
sort(res.begin(), res.end()); //所取到的结果是从左右两边取得元素,所以要重新排一下序
return res;
}
};
下面这种解法,参考@Grandyang博主的博客。很巧妙,核心思想:从左右两边的边界开始,如果左边与x的差值要小于或等于右边的(题目中要求,如差值一样,取元素的较小值),则去掉右边界的元素;若反之, 去掉左边的元素.......直到数组中的只剩下k个元素,即为我们所求的,确实很精辟。
class Solution {
public:
vector<int> findClosestElements(vector<int>& arr, int k, int x){
vector<int> res = arr;
while (res.size() > k){
if (x - arr.front() <= arr.back() - x)
res.pop_back();
else
res.erase(res.begin());
}
return res;
}
};