Writing a generic binary search method - java

I have written this binary search method that returns the index of the Book object in an ArrayList where the book id matches the inputted book id.
How can I turn this into a generic method that takes a different type of ArrayList of object and search input as params and searches against that input? Is there a way I can generalize it?
public static int bSearch(ArrayList<Book> a, String input)
{
int low = 0;
int high = a.size() - 1;
int index = -1;
while(low <= high)
{
int mid = (low + high) / 2;
if(input.compareTo(a.get(mid).getID()) == 0) //input == target
{
//a binary search that returns index of min or max
index = mid;
return index;
}
else if(input.compareTo(a.get(mid).getID())) < 0) //input < target
high = mid - 1;
else if(input.compareTo(a.get(mid).getID()) > 0) //input > target
low = mid + 1;
}
return index;
}

Do note that you're reinventing the wheel, a generic binary search is already implemented in Collections.binarySearch.
With that in mind, for the sake of an exercise, sure it's possible to transform your implementation to make it generic, with a couple of changes:
Declare type parameter <T>
Make the elements of the input list have type T instead of Book (strictly speaking, ? extends T will be ideal)
Make the type of the element to search for T
Add a comparator parameter, and use it to replace the comparisons on Book::getID
Like this:
public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> comparator) {
int low = 0;
int high = list.size() - 1;
int index = 0;
while (low <= high) {
int mid = (low + high) / 2;
int cmp = comparator.compare(key, list.get(mid));
if (cmp == 0) {
index = mid;
return index;
} else if (cmp < 0) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return index;
}
When you use this function to find a Book in a list by ID, you can write the comparator parameter as Comparator.comparing(Book::getID).
(Needless to say, the list parameter must be sorted by book ids already, otherwise binary search won't make sense.)
Lastly, as #nic pointed out in a comment, your implementation has a very undesirable behavior: when an element is not found, it returns 0. This is not so good, for two reasons:
It's impossible to tell if the element was found in the first position of the list, or not found. On a return value of 0, the caller would have to verify if the first element of the list (if exists) is equal to the searched element. That's ugly and painful.
It doesn't give a hint about the position where the missing element could fit in if inserted, which is a very interesting piece of information.
When the element is not found in the list, the common practice is to return -1 -index, where index is the position where the element should be if it was in the sorted list. You can implement this by changing the last line of the method:
return -1 - low;
For your follow-up question, if you wanted this method to take a book id string as the key to search for, then instead of a comparator, the third parameter could be a function that extracts the key from book instances. Then, instead of comparing book instances by a comparator, you could compare the key with the key extracted from instances:
public static <T> int binarySearchByStringField(List<? extends T> list, String key, Function<T, String> keyExtractor) {
int low = 0;
int high = list.size() - 1;
int index = 0;
while (low <= high) {
int mid = (low + high) / 2;
int cmp = key.compareTo(keyExtractor.apply(list.get(mid)));
if (cmp == 0) {
index = mid;
return index;
} else if (cmp < 0) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1 - low;
}

Related

How can I make this binary search code more efficient?

public class MySearch {
public static int search(MyArray array, int value) {
int index = -1;
int start = 0, end = array.length - 1;
while(start <= end) {
int mid = (start + end) / 2;
if(array.compToValue(mid, value) == 1) end = mid - 1;
else if(array.compToValue(mid, value) == -1) start = mid + 1;
else return mid;
}
return index;
}
}
In some cases the number of comparisons is exceeded as you can see in the screenshot. I'm not allowed to use read operations (get). The number of comparisons I'm allowed to make is O(logn).
Your code says nothing about whether the array is ordered. If the array is not ordered, you can do only linear search, and a search operation in O(log n) is impossible.
If the array is ordered, as Turing85's comment says:
extract array.compToValue(mid, value) into a variable, use this variable in the if-clauses instead of calculating the value twice.
Also useful advice is to always use curly brackets. Java allows to drop it if the code block is only a single line, but doing that is a bad practice.

BinarySearch in a SortedArrayList

I am trying to develop a faster way than what I currently have to add an element to a sorted array list. Currently this is my strategy
public void insertSorted(E value) {
add(value);
for (int i = size() - 1; i > 0 && value.compareTo(get(i - 1)) < 0; i--) {
this.swap(i);
}
}
and my add method...
public void add(E element) {
ensureCapacity();
array[size++] = element;
}
So I read that using a binary search algorithm I could more efficiently find the best way to put an element even faster.
I tried developing that, but somehow it always outputs me 0.
private int binarySearch(E value) {
int low = 0;
int high = this.size()-1;
while (low <= high) {
int mid = (low + high) / 2;
E midVal = this.get(mid);
int cmp = midVal.compareTo(value);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid;
}
return low;
}
public void insertSorted(E value) {
int searchResult = binarySearch(value);
add(value, searchResult);
System.out.println("Value: " + value + ". Position = " + searchResult);
}
Could someone help me out? If necessary I will show full code
Rather than developing your own binary search, use built-in Arrays.binarySearch implementation. However, this wouldn't give you much improvement over your original version in terms of time.
To see why, consider the steps that you take to place the value in the sorted sequence:
Find the insertion position
Move items to the right of insertion position by one
Place the element into insertion position
The first step can be done in O(log2N). The second step takes O(N). The last step takes O(1). Overall, insertion's time complexity is O(log2N + N + 1), which is the same as O(N). The algorithm is dominated by the second step, so you might as well use linear search as you move items to the right by one.

How to use binary search of a sorted list and then count the comparisons

I am trying to use my sorted list and implement it with binary search. Then i want to count the number of comparisons it takes to find the key. my code is:
public class BinarySearch {
private static int comparisions = 0;
public static void main(String[] args) {
int [] list = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
int i = BinarySearch.BinSearch(list, 20);
System.out.println(comparisions);
}
public static int BinSearch(int[] list, int key) {
int low = 0;
int high = list.length - 1;
int mid = (high + low) / 2;
if (key < list[mid]) {
high = mid - 1;
comparisions++;
} else if (key == list[mid]) {
return mid;
comparisions++;
} else {
low = mid + 1;
comparisions++;
}
return -1;
}
}
So far it only gives me 1 for the comparison no matter what number is the key.
Your code is missing the looping part of the search, that looping can either be done using recursion or using a while loop. In both cases you have to ask yourself wether or not you just want to know the count or actually return the count of comparisons. Since your method right now returns the index, it cannot easily return the count of comparisons at the same time. For that to work you either need to return an array of two ints or a custom class IndexAndComparisonCount { ... }.
If you use a recursive approach you need to increment whenever you do a comparison and when you do a recursive call you need to get the return value of that recursive call and increment the comparisonCount the call returned by 1:
if (... < ...) {
IndexAndComparisonCount ret = BinSearch(...);
ret.comparisonCount += 1;
return ret;
} else if (... > ...) {
IndexAndComparisonCount ret = BinSearch(...);
ret.comparisonCount += 2; // since you did compare twice already
return ret;
} else {
return new IndexAndComparisonCount(mid, 2); // you compared twice as well
}

What counts as a binary search comparison?

I'm writing a program that determines how many comparisons it takes to run a binary search algorithm for a given number and sorted array. What I don't understand is what counts as a comparison.
// returns the number of comparisons it takes to find key in sorted list, array
public static int binarySearch(int key, int[] array) {
int left = 0;
int mid;
int right = array.length - 1;
int i = 0;
while (true) {
if (left > right) {
mid = -1;
break;
}
else {
mid = (left + right)/2;
if (key < array[mid]) {
i++;
right = mid - 1;
}
else if (key > array[mid]) {
i++;
left = mid + 1;
}
else {
break; // success
}
}
}
return i;
}
The function returns i, which is supposed to be the total number of comparisons made in finding the key in array. But what defines a comparison? Is it any time there is a conditional?
Thanks for any help, just trying to understand this concept.
Usually, a comparison occurs each time the key is compared to an array element. The code seems to not be counting that, though. It is counting how many times one of the search boundaries (left or right) is changed. It's not exactly the same thing being counted, but it's pretty close to the same thing, since the number of times a boundary is shifted is directly related to the number of times through the loop and hence to the number of times a comparison is made. At most, the two ways of counting will be off by 1 or 2 (I didn't bother to figure that out exactly).
Note also that if one were to use the usual definition, the code could be rewritten to use Integer.compare(int,int) do a single comparison of key with array[mid] to determine whether key was less than, equal to, or greater than array[mid].
public static int binarySearch(int key, int[] array) {
int left = 0;
int mid;
int right = array.length - 1;
int i = 0;
while (left <= right) {
mid = (left + right)/2;
int comp = Integer.compare(key, array[mid]);
i++;
if (comp < 0) {
right = mid - 1;
}
else if (comp > 0) {
left = mid + 1;
}
else {
break; // success
}
}
return i;
}

Binary Search Algorithm from a Sorted list

My algorithm is suppose to tell me if 'x'(which has the value 5) is in the sorted array. However, I keep getting a 0. Well since my condition states that if 'x' is not in the array show 0. Where am I going wrong?
import java.util.Arrays;
public class binarySeacg {
public static void main (String[]args)
{
int[] array = {10,7,11,5,13,8};
exchangesort(array);
binsearch(array,5);
System.out.println(Arrays.toString(array));
}
public static void exchangesort(int[] S)
{
int i,j,temp;
for(i=0;i<S.length;i++)
for(j=i+1;j<S.length;j++)
if(S[i]>S[j])
{
temp = S[i];
S[i] = S[j];
S[j] = temp;
}
}
public static int binsearch(int[] S, int x)
{
int location, low, high, mid;
low = 1; high = S.length;
location = 0;
while(low<=high && location==0)
{
mid =(low + high)/2;
if(x== S[mid])
location = mid;
else if(x < S[mid])
high = mid -1;
else
low = mid + 1;
}
System.out.println(location);
return location;
}
}
You set low = 1;, and 5 is the minimal element - so it is in index 0 - so in the sublist of [1,S.length] - it is indeed not there.
You should set low = 0;, and start from the first element - not the second. (Remember that index in java starts from 0, not 1).
(PS, note that in this specific case - the algorithm is correct, since in the sorted list - 5 is in the index 0).
Here you are sorting an array and then the sorted array is used for searching the element.
And if the search is successful, then you do the below assignment
location = mid; which means you are assigning the matching element's index to the location variable.
In this case, element 5 is in 0th index.
Hence you are always getting 0 on your STDOUT
Because, you are trying to find x value, which you are passing 3 and in your list. It is not present. So, change it to other value like 5 and then try.
Also, you should start low=0 instead of low=1. Because, it will miss the first element all the time.
public static int binsearch(int[] S, int x)
{
int location, low, high, mid;
low = 0; high = S.length;
location = 0;
while(low<=high && location==0)
{
mid =(low + high)/2;
if(x == S[mid])
{
location = mid;break;
}
else if (x < S[mid])
{
high = mid - 1;
} else
{
low = mid + 1;
}
}
System.out.println(location);
return location;
}
Note : For the different output, change the value binsearch(array,5); here, which is called from main() method. Remember, change the value, which are present in your list.
in java and most languages, the index starts from 0, not 1, and ends at n-1, not n
for binary search, check carefully about when exiting the while loop, and always remember the meaning of your low and high variables, whether it is [low, high], or [low, high)
for this specific problem, u should also consider what if there r duplicates in the array. whether to return the first element or anyone in the array

Categories

Resources