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);
}
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.
}
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.
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.
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);
}
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));
}
}
Related
I'm trying to search through an array of DrillingRecord objects where each object contains an array within itself. I have to find a value at a specific index in the internal array for each object, and then output the array as well as keep track of matches. The search is partially working as it will find some values but not all of them.
//record is empty. I put it there so I wouldn't have to call it every time.
public String binarySearch( int index, int first, int last, double key, int amount, DrillingRecord record) {
if (last >= first) {
int mid = first + (last - first) / 2;
if (mid < count) { //count is size of the array of objects, data[]
for (int j = 0; j <= mid; j++) {
record = (DrillingRecord) data[j]; //Object[] array hence the cast
double[] dataRecord = record.getNums(); //getNums returns array of doubles
if (dataRecord[index-2] == key) { //index-2 is not the problem
amount++;
System.out.println(record.noErrors()); // noErrors is basically toString
}
}
record = (DrillingRecord) data[mid];
double[] dataRecord = record.getNums();
if (dataRecord[index-2] < key ) {
return binarySearch(index, first, mid+1, key, amount, record);
} else if (dataRecord[index-2] > key) {
return binarySearch(index, mid-1, last, key, amount, record);
}
}
}
if (amount == 0) {
return "-1 /n Drilling records found: " + amount;
}
return "Drilling records found: " + amount;
}
For example, I know there are 4 records with a value of 12.0 at index 3, but when I run the program it says it only found 2. I've been troubleshooting this for a while, and I suspect it has something to do with the for loop I'm just not quite sure what that is.
Thank you for any help it's much appreciated.
i have a problem in understanding the following:
public int sort(char[] arr, int index)
{
if(!isEmpty())
{
index = leftChild.sort(arr,index);
arr[index++] = getContent().getToken();
index = rightChild.sort(arr,index);
}
return index;
}
Why is that working but not this:
public void sort(char[] arr, int index)
{
if(!isEmpty())
{
leftChild.sort(arr,index);
arr[index++] = getContent().getToken();
rightChild.sort(arr,index);
}
}
And i also dont understand this : index = leftChild.sort(arr,index); What does this do? Can you guys pls give me examples?
Thanks for your help,
Kiimarii
One more question, i have a method that prints the longest way from the root to a leaf of the binaryTree:
public int height()
{
if ( !isEmpty() )
{
int leftHeight = leftChild.height();
int rightHeight = rightChild.height();
if ( leftHeight > rightHeight )
{
return leftHeight + 1;
} else {
return rightHeight + 1;
}
} else {
return 0;
}
}
But how can leftHeight > rightHeight be done, if no one has a value? they are both zero or something so how can he compare that? Thanks !
Your method performs an in-order traversal of a binary search tree.
The first snippet first calls leftChild.sort(arr,index), which assigns the left sub-tree to the input array. It returns the next index to be assigned.
Then you assign getContent().getToken() of the current node to the arr[index]
Then the call to rightChild.sort(arr,index) assigns the right sub-tree to the input array and the next index to be assigned is returned.
If you ignore the index returned by the recursive call (as you do in the second snippet), arr[index++] = getContent().getToken(); will always assign a value to the 0 index of the array (assuming the initial call is (sort(arr,0))).
You must assign the returned index to the local index variable in order to assign getContent().getToken() to the correct index of the array.
The key is that index is a local variable. Let's follow a really simple example:
4
/ \
2 6
/
1
// First call
(4).sort(arr, 0);
// The left child of (4) is (2)
// (Note that each 'index' here
// is a local variable.)
index = (2).sort(arr, 0);
// We've just called sort again
index = (1).sort(arr, 0)
// and again
index = (empty).sort(array, 0)
// isEmpty() is true
// so return index
return 0
<=
index = 0
// assign contents of (1) to
// arr[0] and increment 0
arr[index++] = getContent().getToken();
// Process right child of (1)
// (index returns unchanged)
index = (empty).sort(arr, 1);
return 1
<=
index = 1
...
We're now in the call to (2).sort so its contents get assigned to index 1 of the array. Can you see how the sequence will complete in order?
I was wondering if it is possible to find the closest lower element in a non-empty sorted array for an element that may be there or may not be there. Elements can be repeated also any number of times. All elements of the array +ve.
For example, if we had the values [2,5,6,7,7,8,9] and we are looking for the element closest lower to 6, it should return 5, because 5 is the biggest number in the array, that is smaller than 6.
Similarly, if we're looking for the element closest lower to 9, it should return 8, because 8 is the biggest number in the array, that is smaller than 9.
And if the closest lower element is not found, return -1 like if we're looking for the element closest lower to 1, it should return -1, because there is no such element which can be lower than 1. Here -1 represents that there's no such value is present in the array which is closest lower to the element
I have tried this below code. Is it all right? If I'm missing something, please help me. Java code will be more helpful.
static int find(int[] a, int target)
{
int n = a.length;
if(target <= a[0])
return -1;
if(target > a[n-1])
return a[n-1];
int i=0,j=n,mid=0;
while(i<j)
{
mid = (i+j)/2;
if(target <= a[mid])
{
if( mid >0 & target> a[mid-1] )
{
return a[mid-1];
}
j= mid;
}
else
{
if( mid<(n-1) & target > a[mid+1] )
{
return a[mid+1];
}
i= mid+1;
}
}
return mid;
}
Using streams:
import java.util.stream.IntStream;
public class FindNearestLowestValue {
public final static void main(String[] args) {
int[] array = {2,5,6,7,7,8,9};
int searchVal = 6;
// reverse the order so the first element of the filtered stream is the result
System.out.println(
IntStream.range(0, array.length)
.map(i -> array[array.length - 1 - i])
.filter(n -> n < searchVal)
.findFirst().orElse(-1)
);
}
}
There exists a standard binarySearch function.
static int find(int[] a, int target) {
int position = Arrays.binarySearch(a, target);
if (position >= 0) {
System.out.println("Found at index " + position);
} else {
int insertIndex = ~position;
System.out.println("Insert position at index " + insertIndex);
position = insertIndex;
}
return position;
}
When not found it delives the ones-complement of the insert position, as shown above. This means when the result is negative, the item is not found.
It does more or less what you did, but on not finding, it cleverly return a negative: ~ insert position (or -insert position - 1).
/**
* Search the next smaller array element.
* #param a the array sorted in ascending order.
* #param target the value to keep below.
* #return the greatest smaller element, or -1.
*/
static int findNextSmaller(int[] a, int target) {
int i= Arrays.binarySearch(a, target);
if (i >= 0) {
--i;
while (i>= 0 && a[i] == target) {
--i;
}
} else {
i = ~i;
--i;
}
return i == -1 ? -1 : a[i];
}
Or, as int is discrete:
static int findNextSmaller(int[] a, int target) {
int i= Arrays.binarySearch(a, target - 1);
if (i >= 0) {
return target - 1;
}
i = ~i;
--i;
return i == -1 ? -1 : a[i];
}
I am trying to create a binary search function and I keep getting stuck in a loop. I am confined to using such magnets. The program already gives me elements to search for. The code below creates an infinite loop.
public class Student < T extends Comparable <? super T >> {
public int binarySearchIter(T[] data, T key) {
int first = 0;
int last = data.length - 1;
int mid, result;
mid = (first + last) / 2;
result = key.compareTo(data[mid]);
while (first <= last) {
if (result == -1) {
return -1;
} else if (result > 0) {
first = mid + 1;
} else {
last = mid - 1;
}
}
return mid;
}
The contract of the compareTo method is to return a positive integer, 0 or a negative integer depending on whether the number is greater, equal or less than the given number. Those numbers might or might not be -1, so you must not rely on that.
Therefore, you should change your while loop like this:
If the result is negative, the key is before the middle element so we update last to before the middle index.
If the result is positive, the key is after the middle element so we update first to after the middle index.
If the result is zero, we just found the correct index.
Also, you are not updating the middle element once you have updated the first and last variable so you should move that code inside the while loop.
public static <T extends Comparable <T>> int binarySearchIter(T[] data, T key) {
int first = 0;
int last = data.length - 1;
int mid, result;
while (first <= last) {
mid = (first + last) / 2;
result = key.compareTo(data[mid]);
if (result < 0) {
last = mid - 1;
} else if (result > 0) {
first = mid + 1;
} else {
return mid;
}
}
return -1;
}
If this is supposed to be a binary search, while loop should start just above mid = (first + last) / 2;
Plus the note from Tunaki.
I have this but the method isn't showing up on the object created in bluej.
How can an do a binary search on an int array then output the found int?
public static int binarySearch(int a[], int element)
{
int first = 0;
int upto = a.length;
while (first < upto)
{
int mid = (first + upto) / 2; // Compute mid point.
if (element < a[mid])
{
upto = mid; // repeat search in bottom half.
} else if (element > a[mid])
{
first = mid + 1; // Repeat search in top half.
}
else
{
return mid; // Found it. return position
}
}
return -(first + 1); // Failed to find key
}
You made the method static. Therefore it most likely appears in the context menu of the class not of the object.
If you always returned found the int you are looking for, you would just return element every time. There would be no point doing a search. This method returns the index of the elements or the negative of the place it is inserted.
BTW: This code example has an old bug in the
int mid = (first + upto) / 2
which should read
int mid = (first + upto) >>> 1;
as it does in Arrays.binarySearch();