给定一个有序数组,两个整数k和x,找到数组中最接近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:通过二分查找的变种解法,先找到第一个大于等于x的数字,然后左右比较谁离x近,就依次返回对应的下标,一直左右延伸到res中有k个数字。
参考代码:
vector<int> findClosestElements(vector<int>& arr, int k, int x) { vector<int> res; if (arr[0] >=x ) { for (int i = 0; i < k; i++) { res.push_back(arr[i]); } return res; } if (arr[arr.size()-1] <= x) { for (int i = (arr.size() - k); i < arr.size(); i++) { res.push_back(arr[i]); } return res; } int left = 0; int right = arr.size() - 1; while (left < right) { int mid = left + (right - left) / 2; if (arr[mid] < x) { left = mid + 1; } else { right = mid; } } int slow = left-1; int fast = left; while (res.size() != k && slow>=0 && fast<arr.size()) { if (abs(arr[slow] - x) < abs(arr[fast] - x)) { res.push_back(arr[slow]); slow--; } else if (abs(arr[slow] - x) > abs(arr[fast] - x)) { res.push_back(arr[fast]); fast++; } else { if(slow==fast){ res.push_back(arr[slow]); slow--; fast++; } else{ res.push_back(arr[slow]); slow--; } } } if (res.size() == k) { sort(res.begin(), res.end()); return res; } while (res.size() != k) { if (slow >= 0) { res.push_back(arr[slow]); slow--; } else { res.push_back(arr[fast]); fast++; } } sort(res.begin(), res.end()); return res; }
思路二:也是二分法,可以直接返回需要的k个数字的最左的下标,首先初始化left=0,right=arr.size()-k,保证在二分过程中不会越界,每次比较arr[mid]和arr[mid+k]哪个离x近,如果arr[mid+k]近,那么意味着如下不变约束成立:
abs(arr[mid+k+d]-target)<abs(arr[mid+d]-target) 0<=d<k
即对以arr[mid+k]为起点向右数k个数肯定比以arr[mid]为起点向右数k个数更靠近target。所以以arr[mid]为起点及小于mid的部分肯定不是最优解,所以抛弃mid的左半部分(包含mid),即left=mid+1。同理对于另外一边也是相同的思想,最后的结论是right=mid。这里要注意死循环处理。即:
abs(arr[mid+k+d]-target)>=abs(arr[mid+d]-target)
这里一定要包含等于符号,否则对于如下的输入[1,2,3,4,5] k=4,target=3。最后会出现两个满足条件的结果[1,2,3,4]和[2,3,4,5]由于题目要求当距离相等时,优先考虑左边,所以等于符号的左右便是当出现距离的情况是,让right=mid,也罢右边界收缩到mid,从而保留左半部分。
参考代码如下:
vector<int> findClosestElements(vector<int>& arr, int k, int x) { int left = 0; int right = arr.size() - k; while (left < right) { int mid = left + (right - left) / 2; if (abs(arr[mid] - x) <= abs(arr[mid + k] - x)) { right = mid; } else { left = mid+1; } } return vector<int>(arr.begin()+left, arr.begin() + left+k); }