Problem
Given a sorted integer array and two integers k and x, return the k integers from the array that are closest to x, in sorted order. When two integers are equidistant from x, the smaller one is preferred.
- Input:
arr = [1, 2, 3, 4, 5],k = 4,x = 3 - Output:
[1, 2, 3, 4] - Explanation: Elements 1 and 5 are both distance 2 from x=3, so 1 is preferred, making [1,2,3,4] the optimal window.
Counter-example: arr = [1, 2, 3, 4, 5], k = 4, x = 5 โ [2, 3, 4, 5]. The four rightmost elements are chosen because 1 is farther from x=5 than any of 2โ5.
Intuition
Because the array is sorted, the k closest elements always form a contiguous window โ there is never a reason to skip over an element to grab a farther one. The problem reduces to finding where that window starts. Both approaches below exploit this contiguity: one shrinks from the outside, the other binary searches for the left boundary directly.
Approach 1 โ Two Pointers
Start with the full array and repeatedly remove the endpoint that is farther from x until exactly k elements remain. On a tie, remove from the right so the smaller (left) element is kept.
- Initialize
left = 0,right = len(arr) - 1. - While the window contains more than
kelements: - If
arr[left]is farther fromxthanarr[right], incrementleft. - Otherwise, decrement
rightโ this handles ties by discarding the larger endpoint. - Return
arr[left : right + 1].
1class Solution:
2 def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]:
3 left, right = 0, len(arr) - 1
4 while right - left + 1 > k:
5 # Discard the farther endpoint; tie goes to right so the smaller left element survives
6 if abs(arr[left] - x) > abs(arr[right] - x):
7 left += 1
8 else:
9 right -= 1
10 return arr[left:right + 1]Time: O(n โ k) โ exactly n โ k removals are performed, one per iteration.
Space: O(1) โ only two index variables; the output slice is not counted.
Approach 2 โ Binary Search on Window Start
Binary search directly for the optimal left boundary of the k-element window. At each midpoint, compare the signed distance from x to the window's left endpoint against the distance to the element just past the window's right endpoint โ no abs() needed.
- Set
left = 0,right = len(arr) - kโ these are the only valid starting indices for a k-element window. - While
left < right, computemid = (left + right) // 2. - If
x - arr[mid] > arr[mid + k] - x, the right neighbor is closer to x, so shift the window right:left = mid + 1. - Otherwise,
arr[mid]is at least as good a start as anything to its right:right = mid. - Return
arr[left : left + k].
1class Solution:
2 def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]:
3 left, right = 0, len(arr) - k
4 while left < right:
5 mid = (left + right) // 2
6 # Signed comparison: no abs() needed โ naturally handles x outside the array
7 # and breaks ties in favor of the smaller (leftmost) element
8 if x - arr[mid] > arr[mid + k] - x:
9 left = mid + 1 # right endpoint is closer; slide window right
10 else:
11 right = mid # mid is still a valid candidate; narrow from the right
12 return arr[left:left + k]Time: O(log(n โ k) + k) โ binary search over n โ k positions, then O(k) to build the result slice.
Space: O(1) โ only two index variables; the output does not count.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Two Pointers | O(n โ k) | O(1) | Simpler to implement; efficient when k is close to n |
| Binary Search | O(log(n โ k) + k) | O(1) | Preferred when n is large and k is small โ far fewer comparisons |
Common Mistakes
- Binary searching over
[0, n-1]instead of[0, n-k]โ the window accessesarr[mid + k], which goes out of bounds once the start index exceedsn - k. - Flipping the comparison direction โ writing
arr[mid + k] - x > x - arr[mid]shifts the window the wrong way; tracearr = [1,3], k = 1, x = 2to confirm your condition moves toward the correct answer. - Adding
abs()to the binary search condition โ the signed expressionx - arr[mid] > arr[mid + k] - xalready handles x outside the array range and correctly breaks ties by favoring the leftmost (smaller) element; wrapping inabs()breaks that tie-breaking guarantee. - Off-by-one in the result slice โ after binary search converges, return
arr[left : left + k], notarr[left : right + 1]; usingleft + kis unambiguous and avoids confusion with the search variableright. - Two-pointer tie-breaking error โ using
>=inabs(arr[left] - x) >= abs(arr[right] - x)removes from the left on ties, which discards the smaller element; the condition must be strict>so ties fall through to remove from the right.
Related Problems
kth-largest-element-in-an-arrayโ k-selection concept without the sorted-array structure; uses quickselect or a heap instead of a sliding windowk-closest-points-to-originโ same k-closest pattern applied to 2D Euclidean distance, where a heap or quickselect replaces binary searchkth-smallest-element-in-a-bstโ exploiting a BST's sorted structure to find the k-th closest to the minimum via in-order traversalfind-peak-elementโ binary search where the partition compares adjacent elements; the same left/right boundary narrowing template applieskoko-eating-bananasโ binary search on the answer value rather than the array index; builds intuition for choosing the right search space