LC658.找到K个最接近的元素
思路
- 二分查找 + 双指针
- 先利用二分查找,找到>= x的第一个值(>= x区间的左端点),记为pos。但是pos位置的值,未必是离x最近的值。可以在排除特殊情况pos == 0 || pos == len - 1之后,比较arr[pos - 1]和arr[pos]离x哪个更近,更近的那个更新为pos位置。如果一样近,就选择左边的那个更新成pos位置。
- 左指针 - lp,右指针- rp,起始时刻都指向离- x最近的位置- pos。然后左右移动两个指针,使得区间长度恰好为- k,然后返回数组切片- arr[lp:rp]即可。这一点看似简单,其实细节极其繁琐,不好弄。需要关注的细节如下。- 特殊情况:如果左指针到达边界lp == 0,那么直接动右指针即可;如果右指针到达边界rp == len - 1,那么直接移动左指针即可。
- 一般情况下的移动规则:左右两根指针的下一个位置arr[lp - 1]和arr[rp + 1],哪一个离x近,哪一个就往边缘动一格。
- 最后的终止条件, - lp和- rp位置都是需要取进去的,所以长度- rp - lp + 1 == k,就说明满足长度条件,从而退出循环。- 代码- class Solution {
- public:
- int findPos(vector<int>& arr, int x) {
- int left = 0, right = arr.size() - 1;
- while (left < right) {
- int mid = (left + right) / 2;
- if (arr[mid] >= x) {
- right = mid;
- } else {
- left = mid + 1;
- }
- }
- if (arr[left] >= x) {
- return left;
- } else {
- return -1;
- }
- }
- vector<int> findClosestElements(vector<int>& arr, int k, int x) {
- // 找到大于等于 x 的第一个数的下标
- int n = arr.size();
- int pos = findPos(arr, x);
- // cout << pos << endl;
- if (pos == -1) {
- return {arr.end() - k, arr.end()};
- } else if (pos == 0) {
- return {arr.begin(), arr.begin() + k};
- }
- // 确保 pos 是离得最近的点
- // 如果距离一致,优先选左边
- pos = (abs(arr[pos] - x) < abs(arr[pos - 1] - x) ? pos : pos - 1);
- int lp = pos, rp = pos;
- while (rp - lp + 1 < k) {
- if (lp == 0) {
- rp += 1;
- continue;
- } else if (rp == n - 1) {
- lp -= 1;
- continue;
- }
- if (abs(x - arr[lp - 1]) <= abs(arr[rp + 1] - x)) {
- lp -= 1;
- } else {
- rp += 1;
- }
- }
- // [lp, rp]包头包尾
- return {arr.begin() + lp, arr.begin() + rp + 1};
- }
- };
 
 
- 特殊情况:如果左指针到达边界
 
 
                         
                                

