658. Find K Closest Elements - Medium

本文介绍了一种在已排序数组中查找k个最接近指定元素x的高效算法,通过两种方法实现:直接比较首尾元素并逐步剔除与x距离较远的元素,以及利用二分搜索定位,快速确定包含最接近元素的子数组范围。

Given a sorted array, two integers k and x, find the k closest elements to x in the array. The result should also be sorted in ascending order. If there is a tie, the smaller elements are always preferred.

Example 1:

Input: [1,2,3,4,5], k=4, x=3
Output: [1,2,3,4]

 

Example 2:

Input: [1,2,3,4,5], k=4, x=-1
Output: [1,2,3,4]

 

Note:

  1. The value k is positive and will always be smaller than the length of the sorted array.
  2. Length of the given array is positive and will not exceed 104
  3. Absolute value of elements in the array and x will not exceed 104

 

M1: 找出k个最近的元素 <==> 在原数组中删除n-k个最远的元素

因为数组有序,离x最远的元素肯定在首、尾出现。先把arr元素复制到新的res arraylist里,每次比较首、尾元素与x之差。如果末尾元素与x之差更大,就去掉末尾元素;如果首元素与x之差更大,就去掉首元素。循环结束条件是res的大小 <= k.

时间:O(N),空间:O(1)

class Solution {
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
        List<Integer> res = new ArrayList<>();
        for(int i = 0; i < arr.length; i++) {
            res.add(arr[i]);
        }
        if(arr == null || arr.length == 0) return res;
        
        while(res.size() > k) {
            if((x - res.get(0)) <= (res.get(res.size() - 1) - x))
                res.remove(res.get(res.size() - 1));
            else
                res.remove(res.get(0));
        }
        return res;
    }
}

 

 

M2: 改进,用binary search(上面的方法太慢了)

定义一个区间,l = 0, r = arr.length - k,每次比较 中点m所在元素与x之差 和 m+k所在元素与x之差,如果 中点m所在元素与x之差 更大,说明m右侧的元素与x之差更小,l = m+1;反之如果 m+k所在元素与x之差 更大,说明m左侧的元素与x之差更小,r = m。最后的l就是所求区间的左边界,返回区间[l, l+k)

时间:O(logN),空间:O(1)

class Solution {
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
        List<Integer> res = new ArrayList<>();
        if(arr == null || arr.length == 0) return res;
        
        int l = 0, r = arr.length - k;
        while(l < r) {
            int m = l + (r - l) / 2;
            if((x - arr[m]) > (arr[m+k] - x))
                l = m + 1;
            else
                r = m;
        }
        for(int i = l; i < l + k; i++) {
            res.add(arr[i]);
        }
        return res;
    }
}

 

二刷:

首先用binary search找到 <= target 的最大元素,其下标作为left, right = left + 1

然后从中间往两边找,left, right两个指针,谁小(和target之差小)移谁

time: O(log(n) + k), space: O(1)

class Solution {
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
        int left = 0, right = arr.length - 1;
        while(left + 1 < right) {
            int mid = left + (right - left) / 2;
            if(arr[mid] == x)
                left = mid;
            else if(arr[mid] > x)
                right = mid;
            else
                left = mid;
        }
        int tmp;
        if(arr[right] <= x) tmp = right;
        if(arr[left] <= x) tmp = left;
        else tmp = -1;
        
        left = tmp;
        right = left + 1;
        
        List<Integer> res = new ArrayList<>();
        for(int i = 0; i < k; i++) {
            if(left >= 0 && right <= arr.length - 1 && Math.abs(arr[left] - x) <= Math.abs(arr[right] - x))
                left--;
            else if(left >= 0 && right > arr.length - 1)
                left--;
            else
                right++;
        }
        for(int i = left + 1; i < right; i++) {
            res.add(arr[i]);
        }
        return res;
    }
}

 

转载于:https://www.cnblogs.com/fatttcat/p/10062228.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值