658. Find K Closest Elements - Medium

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

 

三刷:

first run a binary search to find the largest element that is smaller or equal to target.

then use the element as the initial point and start scanning both sides by using two pointers, the principle is: move the pointer if its corresponding element is closer to the target. continue scanning until there're k elements in between two pointers.

finally we get the boundary of the k closest element and add them to the arraylist as result.  

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

class Solution {
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
        List<Integer> res = new ArrayList<>();
        
        int left = 0, right = arr.length - 1;
        while(left + 1 < right) {
            int mid = left + (right - left) / 2;
            if(arr[mid] >= x) {
                right = mid;
            } else {
                left = mid;
            }
        }
        int tmp = -1;           // post-process, find the element closest to x
        if(arr[right] <= x) {
            tmp = right;
        }
        if(arr[left] <= x) {
            tmp = left;
        }
        
        left = tmp;
        right = left + 1;
        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;
    }
}

 

 

二刷:

首先用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;
    }
}

 

 

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;
    }
}

 

posted @ 2018-12-04 09:25  fatttcat  阅读(171)  评论(0编辑  收藏  举报