Reputation: 22123
I am learning binary search in leetcode from problem Find K Closest Elements - LeetCode
Given a sorted array, two integers
k
andx
, find thek
closest elements tox
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:
- The value k is positive and will always be smaller than the length of the sorted array.
- Length of the given array is positive and will not exceed 104
- Absolute value of elements in the array and x will not exceed 104
It's official solution:
Approach 2: Binary Search and Two PointersAlgorithm
The original array has been sorted so we can take this advantage by the following steps.
If the target
x
is less or equal than the first element in the sorted array, the firstk
elements are the result.Similarly, if the target
x
is more or equal than the last element in the sorted array, the lastk
elements are the result.Otherwise, we can use binary search to find the
index
of the element, which is equal (when this list hasx
) or a little bit larger thanx
(when this list does not have it). Then setlow
to its leftk-1
position, andhigh
to the rightk-1
position of thisindex
as a start. The desired k numbers must in this rang [index-k-1, index+k-1]. So we can shrink this range to get the result using the following rules.
If
low
reaches the lowest index0
or thelow
element is closer tox
than thehigh
element, decrease thehigh
index.If
high
reaches to the highest indexarr.size()-1
or it is nearer tox
than thelow
element, increase thelow
index.The looping ends when there are exactly k elements in [low, high], the subList of which is the result.
The implementation
public class Solution {
public List<Integer> findClosestElements(List<Integer> arr, int k, int x) {
int n = arr.size();
if (x <= arr.get(0)) {
return arr.subList(0, k);
} else if (arr.get(n - 1) <= x) {
return arr.subList(n - k, n);
} else {
int index = Collections.binarySearch(arr, x);
if (index < 0)
index = -index - 1;
int low = Math.max(0, index - k - 1), high = Math.min(arr.size() - 1, index + k - 1); #1????
while (high - low > k - 1) {
if (low < 0 || (x - arr.get(low)) <= (arr.get(high) - x)) #2????
high--;
else if (high > arr.size() - 1 || (x - arr.get(low)) > (arr.get(high) - x)) #????
low++;
else
System.out.println("unhandled case: " + low + " " + high);
}
return arr.subList(low, high + 1);
}
}
}
I have 3 questions about the implementation
low = Math.max(0, index - k - 1)
, I assumed it as low = Math.max(0, index - k - 1)
, since low's left position is [index -k +1]
not [index-k-1]
. if (low < 0 || (x - arr.get(low)) <= (arr.get(high) - x)) #2????
, since low was set as low = Math.max(0, index - k - 1),
and it will increase forwards, is it necessary to check low < 0
, the same as else if (high > arr.size() - 1 || (x - arr.get(low)) > (arr.get(high) - x)) #????
while (high - low > k - 1)
, what's the benefit of it over high - low >=k
?Upvotes: 2
Views: 2843
Reputation: 3744
First I want to say, it is the lowest quality official solution I read in Leetcode, as you said, it indeed has many defect in this solution. I will explain it one by one.
For Question1:
you are kind of correct here, we should change Math.max(0, index - k - 1)
to Math.max(0, index - k)
but not Math.max(0, index - k + 1)
. because when the target number x
is not in array and left k
is the result we want, we should start from index - k
.
for example:
Input: [1,2,3,10,11], k=4, x=2
index is 3
, and we should start from index-k=2
For Question2:
you are correct, low < 0
and high > arr.size() - 1
are unnecessary check here. you can just remove it.
For Question3:
high - low > k - 1
is equals to high - low >= k
in integer comparison.
These are just two different programming habits, choose the one you like.
Hope that helps you, and comment if you have further questions. : )
Upvotes: 2