LeetCode 658 Find K Closest Elements (二分)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Tc_To_Top/article/details/82150311

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

题目链接:https://leetcode.com/problems/find-k-closest-elements/description/

题目分析:

方法一:二分大于x的第一个数,然后two point,时间复杂度(2k+logn)

class Solution {
    
    public boolean smallerOrEq(int lval, int rval, int x) {
        return Math.abs(lval - x) <= Math.abs(rval - x);
    }
    
    public int upperBound(int[] arr, int n, int x) {
        int l = 0, r = n - 1, mid = 0, ans = 0;
        if (x <= arr[l]) {
            return l;
        }
        if (x >= arr[r]) {
            return r;
        }
        while (l <= r) {
            mid = (l + r) >> 1;
            if (arr[mid] <= x) {
                l = mid + 1;
            } else {
                ans = mid;
                r = mid - 1;
            }
        }
        return ans;
    }
    
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
        List<Integer> ans = new ArrayList<>();
        int upperPos = upperBound(arr, arr.length, x);
        int l = upperPos, r = upperPos;
        while (r - l < k) {
            if (l > 0 && r < arr.length) {
                if (smallerOrEq(arr[l - 1], arr[r], x)) {
                    l--;
                } else {
                    r++;
                }   
            } else if (l == 0) {
                r++;
            } else {
                l--;
            }
        }
        for (int i = l; i < l + k; i++) {
            ans.add(arr[i]);
        }
        return ans;
    }
}

方法二:直接一次二分,考虑x, arr[mid]和arr[mid+k]这三个值的大小关系,分三种情况

1)arr[mid] <= arr[mid+k] <= x(mid右移)

2)x <= arr[mid] <= arr[mid+k](mid左移)

3)arr[mid] <= x <= arr[mid+k]    => arr[mid+k] - x < x - arr[mid](mid右移);arr[mid+k] - x >= x - arr[mid](mid左移)

容易发现3)的两种情况的移动方向恰好与1)2)一致,因此可以直接二分左边界,时间复杂度(k+logn)

class Solution {
    public List<Integer> findClosestElements(int[] arr, int k, int x) {
        List<Integer> ans = new ArrayList<>();
        int l = 0, r = arr.length - k - 1, mid;
        while (l <= r) {
            mid = (l + r) >> 1;
            if (Math.abs(arr[mid + k] - x) >= Math.abs(x - arr[mid])) {
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        for (int i = l; i < l + k; i++) {
            ans.add(arr[i]);
        }
        return ans;
    }
}

猜你喜欢

转载自blog.csdn.net/Tc_To_Top/article/details/82150311