K Closest Numbers In Sorted Array

update Aug 17, 2017 14:11

LintCode
LeetCode 这个题的要求有些不同,但是整体类似,只是需要结果最终排序。

Given a target number, a non-negative integer k and an integer array A sorted in ascending order, find the k closest numbers to target in A, sorted in ascending order by the difference between the number and target. Otherwise, sorted in ascending order by number if the difference is same.

Example

Given A = [1, 2, 3], target = 2 and k = 3, return [2, 1, 3].

Given A = [1, 4, 6, 8], target = 3 and k = 3, return [4, 1, 6].

Challenge O(logn + k) time complexity.

Basic Idea:

先用二分法找到target,或者距离target最近的元素,然后向两侧扩展找k-1个最接近target的。

Java Code:

    public class Solution {
        /**
         * @param A an integer array
         * @param target an integer
         * @param k a non-negative integer
         * @return an integer array
         */
        public int[] kClosestNumbers(int[] A, int target, int k) {
            // Write your code here
            // find target first
            if (k == 0) return new int[] {};
            int p = 0, r = A.length - 1;
            while (p + 1 < r) {
                int q = (r - p) / 2 + p;
                if (A[q] < target) p = q;
                else r = q;
            }
            int pos = -1;
            if (A[p] == target) pos = p;
            else if (A[r] == target) pos = r;
            else pos = Math.abs(target - A[p]) <= Math.abs(target - A[r]) ? p : r;


            // find k closest numbers
            List<Integer> res = new ArrayList<>();
            res.add(A[pos]);
            int count = 1;
            int start = pos, end = pos;
            while (count < k) {
                if (end == A.length - 1 || start > 0 &&
            Math.abs(target - A[start - 1]) <= Math.abs(target - A[end + 1])) {
                    start--;
                    res.add(A[start]);
                }
                else {
                    end++;
                    res.add(A[end]);
                }
                count++;
            }
            int[] ret = new int[res.size()];
            for (int i = 0; i < res.size(); ++i) {
                ret[i] = res.get(i);
            }
            return ret;
        }
    }

Python Code:

    class Solution:
        # @param {int[]} A an integer array
        # @param {int} target an integer
        # @param {int} k a non-negative integer
        # @return {int[]} an integer array
        def kClosestNumbers(self, A, target, k):
            if k == 0:
                return []
            # find target first
            p, r = 0, len(A) - 1
            while p + 1 < r:
                q = (r - p) / 2 + p
                if A[q] < target:
                    p = q
                else:
                    r = q
            pos = p if abs(A[p] - target) <= abs(A[r] - target) else r # 注意这里是<=

            # 向左右扩展,找k个最近的
            left, right = pos, pos
            count = 1
            ret = [A[pos]]
            while count < k:
                if right == len(A) - 1 or abs(A[left - 1] - target) <= abs(A[right + 1] - target): # 注意这里也是<=
                    left -= 1
                    count += 1
                    ret.append(A[left])
                else:
                    right += 1
                    count += 1
                    ret.append(A[right])

            return ret

update Dec 3, 2017 20:20

Update

优化了一下 left,right 双指针左右扩展筛选的 code 逻辑。优化后的写法和 merge sort 中 merge aux数组时候的逻辑类似,先考虑两指针的edge case的情况,再考虑普遍情况。

Java Code

    class Solution {
        public List<Integer> findClosestElements(int[] arr, int k, int x) {
            int target = binarySearch(arr, x);
            List<Integer> res = new ArrayList<>();
            res.add(arr[target]);
            k--;
            int left = target - 1, right = target + 1;
            while (k > 0) {
                if (left < 0) res.add(arr[right++]);
                else if (right >= arr.length) res.add(arr[left--]);
                else if (Math.abs(arr[left] - x) <= Math.abs(arr[right] - x)) res.add(arr[left--]);
                else res.add(arr[right++]);
                k--;
            }
            Collections.sort(res);
            return res;
        }

        // if target not exist, return left, if no left, return right
        private int binarySearch(int[] arr, int target) {
            int p = 0, r = arr.length - 1;
            while (p + 1 < r) {
                int q = p + (r - p) / 2;
                if (arr[q] < target) p = q;
                else r = q;
            }
            if (arr[p] == target) return p;
            else if (arr[r] == target) return r;
            else return p;
        }
    }

results matching ""

    No results matching ""