题目
给定一个 排序好 的数组 arr ,两个整数 k 和 x ,从数组中找到最靠近 x(两数之差最小)的 k 个数。返回的结果必须要是按升序排好的。
整数 a 比整数 b 更接近 x 需要满足:
- |a - x| < |b - x| 或者
- |a - x| == |b - x|且a < b
示例
输入:arr = [1,2,3,4,5], k = 4, x = 3
输出:[1,2,3,4]
输入:arr = [1,2,3,4,5], k = 4, x = -1
输出:[1,2,3,4]
解法一:滑动窗口
由于题目中数组是已经排序好的,因此最靠近x的数应该是分布在x两侧的连续k个数。返回结果为固定长度窗口,因此可以考虑滑动窗口解法,具体步骤如下:
- **Step1:**初始化一个长度为k的窗口;
- **Step2:**滑动窗口,计算x与左右边界的距离dist。若 distLeft > distRight或窗口内数据值相同,窗口右移,否则,跳出循环;
- **Step3:**返回左右边界内所有数据为所求
具体实现思路如下:
class Solution {
public List<Integer> findClosestElements(int[] arr, int k, int x) {
// 初始化滑动窗口
int size = arr.length;
int left = 0,right = k - 1;
while(right + 1 < size){
// 若窗口内数据相同,右移
if(arr[right + 1] == arr[left]){
++right;
++left;
continue;
}
// 若distLeft > distRight,右移
if(Math.abs(arr[right + 1] - x) < Math.abs(arr[left] - x)){
++right;
++left;
continue;
}
break;
}
// 返回左右边界内数据
List<Integer> ans = new ArrayList<>();
for(int i = left; i <= right; ++i){
ans.add(arr[i]);
}
return ans;
}
}
其运行结果如下:
执行用时:4 ms, 在所有 Java 提交中击败了73.23%的用户
内存消耗:43.6 MB, 在所有 Java 提交中击败了27.85%的用户
解法二:双指针
由于答案一定为连续的数组,因此可以采用双指针的方式,从数组两端向中间逼近,确定最终数组位置。
其实现逻辑如下:
class Solution {
public List<Integer> findClosestElements(int[] arr, int k, int x) {
List<Integer> ans = new ArrayList<>();
// 初始化左右指针和需要移除的元素数量
int left = 0,right = arr.length - 1,removeSize = arr.length - k;
// 当移除数量大于0(即当前窗口大于k),继续逼近
while(removeSize > 0){
// 边界逼近,当相同时优先缩小右边界
if(Math.abs(arr[left] - x) > Math.abs(arr[right] - x)){
++left;
--removeSize;
continue;
}
if(Math.abs(arr[left] - x) <= Math.abs(arr[right] - x)){
--right;
--removeSize;
continue;
}
}
for(int i = left; i <= right; ++i){
ans.add(arr[i]);
}
return ans;
}
}
其运行结果如下:
执行用时:4 ms, 在所有 Java 提交中击败了73.23%的用户
内存消耗:43.2 MB, 在所有 Java 提交中击败了67.11%的用户