题目:
题目链接
给定一个排序好的数组
a
r
r
arr
arr,两个整数
k
k
k和
x
x
x,从数组中找到最靠近
x
x
x(两数之差最小)的
k
k
k个数。返回的结果必须要是按升序排好的。
整数 a a a比整数 b b b更接近 x x x需要满足:
- ∣ a − x ∣ < ∣ b − x ∣ |a - x| < |b - x| ∣a−x∣<∣b−x∣ 或者
- ∣ a − x ∣ = = ∣ b − x ∣ |a - x| == |b - x| ∣a−x∣==∣b−x∣且 a < b a < b 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:
- 定义 h e a d head head指向开头, t a i l tail tail指向第 k k k个数。表示 h e a d head head- t a i l tail tail区间间的数要添加到返回的结果 r e s res res链表中。【添加到 r e s res res里的数一定是在 a r r arr arr中连续的】
- 从第 k k k个数开始向后遍历,判断是否要替换区间里的数
- 如果当前遍历的数<= x x x,则与 x x x距离肯定是比在区间里面的数还要短的。所以把当前遍历的数加进区间里,即区间右移。head++,tail++
- 如果当前遍历的数> x x x,需要判断这个数是否能替换 h e a d head head,因为 h e a d head head是区间里距离 x x x最远的。若能替换,区间右移。不能则后面的数距离只会越来越大, b r e a k break break退出。
- 把区间里的数加入 r e s res res
class Solution {
public List<Integer> findClosestElements(int[] arr, int k, int x) {
int len = arr.length;
List<Integer> res = new LinkedList<>();
//定义区间
int head = 0;
int tail = k-1;
//从第k个数开始往后遍历
for(int i=k;i<len;i++){
if(arr[i]<=x){//第i个数到x的距离比区间任何一个数都短,区间右移
head++;
tail++;
}else{//判断第i个数和head到x的距离来决定要不要移动区间
int num1 = Math.abs(arr[i]-x);
int num2 = Math.abs(arr[head]-x);
if(num2>num1){
head++;
tail++;
}else{
break;
}
}
}
for(int i=head;i<=tail;i++){
res.add(arr[i]);
}
return res;
}
}
思路2:二分加双指针
- 先用二分查找找到两个指针: l e f t left left、 r i g h t right right。 [ 0 , l e f t ] [0,left] [0,left]区间的数都小于 x x x, [ r i g h t , l e n − 1 ] [right,len-1] [right,len−1]区间的数都大于等于 x x x。
- l e f t left left和 r i g h t right right都指向最有可能接近 x x x的元素,注意 l e f t left left和$right指向的数都是备选进入链表的。
- 如果 x − arr [ left ] ≤ arr [ right ] − x x-\textit{arr}[\textit{left}] \le \textit{arr}[\textit{right}]-x x−arr[left]≤arr[right]−x,那么 l e f t left left指向的数入选, l e f t left left–;反之 r i g h t right right指向的数入选, r i g h t right right++。
- 如果 l e f t left left越界,直接 r i g h t right right++;反之 r i g h t right right越界,直接 l e f t left left–。
class Solution {
public List<Integer> findClosestElements(int[] arr, int k, int x) {
int right = BinarySearch(arr,x);
int left = right-1;
while(k-- >0){
if(left<0){
right++;
}else if(right>=arr.length){
left--;
}else if(x-arr[left]<=arr[right]-x){
left--;
}else{
right++;
}
}
List<Integer> res = new LinkedList<>();
for(int i=left+1;i<right;i++){
res.add(arr[i]);
}
return res;
}
public int BinarySearch(int []arr,int x){
int left = 0;
int right = arr.length-1;
while(left<right){
int mid = (right+left)/2;
if(arr[mid]>=x){
right = mid;
}else{
left = mid+1;
}
}
return right;
}
}
思路3:重载compare函数
M a p 、 S e t 、 L i s t Map 、Set 、List Map、Set、List等集合中,都提供了一个排序方法 s o r t ( ) sort() sort()【默认情况下是从小到大排序】。可以重写Comparator 接口中的compare 方法,来使用特定的方式对集合元素进行排序。
官方代码如下:
class Solution {
public List<Integer> findClosestElements(int[] arr, int k, int x) {
List<Integer> list = new ArrayList<Integer>();
for (int num : arr) {
list.add(num);
}
Collections.sort(list, (a, b) -> {
if (Math.abs(a - x) != Math.abs(b - x)) {
return Math.abs(a - x) - Math.abs(b - x);
} else {
return a - b;
}
});
List<Integer> ans = list.subList(0, k);
Collections.sort(ans);
return ans;
}
}
- 自定义排序方式1:
Collections.sort(list, (a, b) -> {
if (Math.abs(a - x) != Math.abs(b - x)) {
return Math.abs(a - x) - Math.abs(b - x);
} else {
return a - b;
}
});
- 自定义排序方式2:
Collections.sort(list, new Comparator<Integer>(){
@Override
public int compare(a, b){
if (Math.abs(a - x) != Math.abs(b - x)) {
return Math.abs(a - x) - Math.abs(b - x);
} else {
return a - b;
}
}
});
- 自定义排序方式3:
list.sort(new Comparator<Integer>(){
@Override
public int compare(a, b){
if (Math.abs(a - x) != Math.abs(b - x)) {
return Math.abs(a - x) - Math.abs(b - x);
} else {
return a - b;
}
}
);
返回值的含义:
- 负整数:a < b , 位置排在前
- 零:a=b,位置不变
- 正整数:a>b,位置排在后