|
| 1 | +""" |
| 2 | +Given a sorted integer array arr, two integers k and x, return the k |
| 3 | +closest integers to x in the array. The result should also be sorted |
| 4 | +in ascending order. |
| 5 | +
|
| 6 | +An integer a is closer to x than an integer b if: |
| 7 | + |a - x| < |b - x|, or |
| 8 | + |a - x| == |b - x| and a < b |
| 9 | +------- |
| 10 | +- Probably a linear time solution? Only need to look at every val once. |
| 11 | +- Can use a maxheap - push tuples in of (distance from x, val); heapify on first value, only |
| 12 | +push new elements when max distance from x is greater than current element's. |
| 13 | +- Input array is sorted, so can we do better? |
| 14 | +- Could find index / insertion point for x in arr, then |
| 15 | + use two pointers to keep track of k nearest elements - O(log(N)) + K where N = len(arr) |
| 16 | +----------- |
| 17 | +ret = collections.deque() |
| 18 | +insertion_point = bin_search(arr, x) |
| 19 | +if arr[insertion_point] <= x: |
| 20 | + lower, higher = insertion_point, insertion_point+1 |
| 21 | +else: |
| 22 | + lower, higher = insertion_point-1, insertion_point |
| 23 | +while len(ret) < k and (lower >= 0 or higher < len(arr)): |
| 24 | + if higher >= len(arr) or distance(x, arr[lower]) <= distance(x, arr[higher]): |
| 25 | + ret.appendleft(arr[lower]) |
| 26 | + lower -= 1 |
| 27 | + else: |
| 28 | + ret.append(arr[higher]) |
| 29 | + higher+=1 |
| 30 | +return ret |
| 31 | +""" |
| 32 | +from collections import deque |
| 33 | +from typing import List |
| 34 | + |
| 35 | +def bin_search(arr, val): |
| 36 | + lo, hi = 0, len(arr)-1 |
| 37 | + while lo <= hi: |
| 38 | + mid = (hi + lo) // 2 |
| 39 | + if arr[mid] == val: |
| 40 | + return mid |
| 41 | + elif arr[mid] > val: |
| 42 | + lo, hi = lo, mid - 1 |
| 43 | + else: |
| 44 | + lo, hi = mid+1, hi |
| 45 | + return lo |
| 46 | + |
| 47 | +class Solution: |
| 48 | + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: |
| 49 | + ret = deque() |
| 50 | + insertion_point = bin_search(arr, x) |
| 51 | + if insertion_point < len(arr) and arr[insertion_point] <= x: |
| 52 | + lower, higher = insertion_point, insertion_point+1 |
| 53 | + else: |
| 54 | + lower, higher = insertion_point-1, insertion_point |
| 55 | + while len(ret) < k and (lower >= 0 or higher < len(arr)): |
| 56 | + if higher >= len(arr) or abs(x - arr[lower]) <= abs(x - arr[higher]): |
| 57 | + ret.appendleft(arr[lower]) |
| 58 | + lower -= 1 |
| 59 | + else: |
| 60 | + ret.append(arr[higher]) |
| 61 | + higher+=1 |
| 62 | + return list(ret) |
| 63 | + |
| 64 | +s = Solution() |
| 65 | +for case in [ |
| 66 | + # lower will go below 0 |
| 67 | + ([1,2,3,4,5], 4, 0, [1,2,3,4]), |
| 68 | + # higher will go above last element |
| 69 | + ([1,2,3,4,5], 4, 6, [2,3,4,5]), |
| 70 | + # len(arr) == k |
| 71 | + ([1,2,3,4,5], 5, 9, [1,2,3,4,5]), |
| 72 | + # x not in arr |
| 73 | + ([1,2,3,4,5], 4, -1, [1,2,3,4]), |
| 74 | + # x in arr |
| 75 | + ([1,2,3,4,5], 4, 3, [1,2,3,4]), |
| 76 | + # previous failures |
| 77 | + ([1,1,2,2,2,2,2,3,3], 3, 3, [2,3,3]) |
| 78 | +]: |
| 79 | + arr, k, x, ans = case |
| 80 | + print(arr, k, x) |
| 81 | + actual = s.findClosestElements(arr, k, x) |
| 82 | + assert actual == ans, (arr, k, x, ans, actual) |
0 commit comments