Reputation: 31
I have a sorted array. Given a key value (not necessarily in the table), I want to find the element in the table that is closes to the key value.
I have considered using a binary search, but I need to return the closest element if the key is not in the table (not -1). What should I try to do?
If there is no matches return -1
. This is my current try with binary search:
public static long binarySearch (ArrayList<Long> arr, int first, int last, long key)
{
if (first > last) return -1;
int mid = first + (last - first)/2;
if (arr.get(mid) == key)
return mid;
else if (arr.get(mid) > key)
return binarySearch(arr, first, mid - 1, key);
else
return binarySearch(arr, mid + 1, last, key);
}
Upvotes: 2
Views: 3764
Reputation: 12207
Fortunately, the Java standard libraries include Arrays.binarySearch
which gives you the "insertion point" of an element if it is not included in an array:
Returns: index of the search key, if it is contained in the array; otherwise, (-(insertion point) - 1). The insertion point is defined as the point at which the key would be inserted into the array: the index of the first element greater than the key, or a.length if all elements in the array are less than the specified key. Note that this guarantees that the return value will be >= 0 if and only if the key is found.
With that we can implement your requirement very concisely:
import java.util.Arrays;
public class ClosestValue
{
static long closestValue(long[] sorted, long key)
{
if(sorted.length==1) {return sorted[0];} // trivial case
if(key<sorted[0]) {return sorted[0];} // lower boundary
if(key>sorted[sorted.length-1]) {return sorted[sorted.length-1];} // upper boundary
int pos = Arrays.binarySearch(sorted, key);
if(pos>=0) {return sorted[pos];} // we found an exact match
// we didn't find an exact match, now we have two candidates: insertion point and insertion point-1 (we excluded the trivial case before)
// pos = -ip-1 | +ip -pos => ip = -pos-1
int ip = -pos-1;
long closest;
if(sorted[ip]-key<key-sorted[ip-1]) {closest=sorted[ip];} // < can be <= if smaller value is preferred
else {closest=sorted[ip-1];}
return closest;
}
public static void main(String[] args)
{
System.out.println(closestValue(new long[] {1,4,6,7,8,19},3));
System.out.println(closestValue(new long[] {1,2,4,5},3));
System.out.println(closestValue(new long[] {1,2,4,5},7));
System.out.println(closestValue(new long[] {1,2,4,5},-5));
}
}
Upvotes: 0
Reputation: 21
This will solve the question, to find the closest value, find the sum of the nearing index in the List, say for example {1,4,6,7,8,19} and key 3. the binary search will have the final subset with 1 and 4,
if (1+4 > 3+3) ? return 1 else return 4
if (first > last)
{
// This makes an Invalid case
return -1;
}
if (first == last)
{
// then get the valueOf(firstIndex)
return arr.get(first-1);
}
if (first + 1 == last)
{
// gets value from the first Index
int fistKey = arr.get(first-1);
// gets value from first Index + 1 i.e next Index
int nextKey = arr.get(first);
// if valueof(firstIndex) + valueOf(nextIndex) > key then,
// key will be closer to valueOf(firstIndex)
// else key will be closer to valueOf(nextIndex)
return ((fistKey + nextKey) > (key + key)) ? fistKey : nextKey;
}
else
{
// assuming List will start its index from 0, then "-1" used for mid calculation
int mid = (last+1)/2;
int keyFromList = arr.get(mid-1);
if (keyFromList == key)
return key;
if (keyFromList > key)
return binarySearch(arr, first, mid , key);
else
return binarySearch(arr, mid, last , key);
}
Upvotes: 0
Reputation: 38132
Try something like (untested):
public static Long getClosest(List<Long> sortedList, Long key) {
int index = Collections.binarySearch(sortedList, key);
Long closest;
if (index >= 0) {
closest = sortedList.get(index);
} else {
index = -index - 1;
if (index == 0){
closest = sortedList.get(index);
} else if (index == sortedList.size()){
closest = sortedList.get(index - 1);
} else {
Long prev = sortedList.get(index - 1);
Long next = sortedList.get(index);
closest = ((key - prev) < (next - key)) ? prev : next;
}
}
return closest;
}
As said, this code is untested and you might have to check if it returns the correct value for all the corner cases.
Upvotes: 1
Reputation: 5661
Change:
if (first > last) return -1;
to
if (first > last) {
// if either first or last is negative, return the first element.
// if either first or last are greater than arr length, return the last element.
// otherwise, get values in the array for indecies first and last, compare then to
// your key and return the closest.
}
Upvotes: 1
Reputation: 461
When element at mid position isn't equal to key, You can calculate delta as abs(key-arr.get(mid)) and check whether it is lowest than actual delta (the lowest delta, the closest value You've got). In the end, if You don't find key in array, You return delta instead -1.
Notice, that You can NOT initialise delta with 0, because any later calculated delta will be greater than 0.
Upvotes: 0