题目
给定一个 排序好 的数组 arr ,两个整数 k 和 x ,从数组中找到最靠近 x(两数之差最小)的 k 个数。返回的结果必须要是按升序排好的。
整数 a 比整数 b 更接近 x 需要满足:
|a - x| < |b - x| 或者
|a - x| == |b - x| 且 a < b示例 1:
输入:arr = [1,2,3,4,5], k = 4, x = 3
输出:[1,2,3,4]示例 2:
输入:arr = [1,2,3,4,5], k = 4, x = -1
输出:[1,2,3,4]提示:
1 <= k <= arr.length
1 <= arr.length <= 10^4
arr 按 升序 排列
-10^4 <= arr[i], x <= 10^4来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/find-k-closest-elements
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
一、最无脑的解法是优先队列,开辟一个大小为k的优先队列,遍历每一个数计算其和x的差值,看能否入队,最后将队列的k个元素排好序返回就行了。见代码一。
二、双指针的解法也比较好想,可以先用找到最接近x的那个数,然后从这个数往两边扩散找够k个最接近x的。
不过,还有不需要二分的双指针思路,直接使用两个指针从数组两端向中间靠拢,那边更接近x就移动哪边的指针。见代码二。
三、直接二分的思路不太好想,可以看liweiwei的「题解」,主要思路是二分结果数组的左边界,因为长度为k的相邻的两个子数组只有端点处不同,所以可以看做是在k+1的子数组两端选择删除一个元素,就等同于左边界的移动。见代码三。
代码
代码一 优先队列
class Solution {public List<Integer> findClosestElements(int[] arr, int k, int x) {PriorityQueue<int[]> minHeap = new PriorityQueue<>(k, (a, b) -> b[1] - a[1]);for (int num : arr) {int diff = Math.abs(num - x);if (minHeap.size() < k) {minHeap.offer(new int[]{num, diff});} else if (diff < minHeap.peek()[1]) {minHeap.poll();minHeap.offer(new int[]{num, diff});}}List<Integer> ans = new ArrayList<>();while (!minHeap.isEmpty()) {ans.add(minHeap.poll()[0]);}Collections.sort(ans);return ans;}}
代码二 双指针
class Solution {public List<Integer> findClosestElements(int[] arr, int k, int x) {List<Integer> ans = new ArrayList<>();int n = arr.length;int l = 0;int r = n - 1;int step = n - k;while (step > 0) {// l指向的数和x差值比r更大,移动l指针if (x - arr[l] > arr[r] - x) {l++;} else {// 否则移动r指针,注意等于时移动r,因为题目要求确保差值相同时数字要更小的r--;}step--;}for (int i = l; i <= r; i++) {ans.add(arr[i]);}return ans;}}
代码三 二分
class Solution {public List<Integer> findClosestElements(int[] arr, int k, int x) {int n = arr.length;int low = 0;int high = n - k;while (low < high) {int mid = low + (high - low) / 2;if (x - arr[mid] > arr[mid + k] - x) {low = mid + 1;} else {// 这里同样等于时删除右边的元素,确保差值相同时数字更小high = mid;}}List<Integer> res = new ArrayList<>(k);for (int i = low; i < low + k; ++i) {res.add(arr[i]);}return res;}}
