leetcode--第658题--中等--《找到 K 个最接近的元素》

题目:

题目链接
给定一个排序好的数组 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| ax<bx 或者
  • ∣ a − x ∣ = = ∣ b − x ∣ |a - x| == |b - x| ax==bx 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,len1]区间的数都大于等于 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 xarr[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 MapSetList等集合中,都提供了一个排序方法 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,位置排在后
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值