I am trying to construct a recursive binary search method that searches a sorted array of comparable objects for an object of interest. This is part of a larger algorithm that searches a collection of sorted arrays and looks for elements common to all arrays in the collection. The goal is to make the search/comparison portion of the algorithm as efficient as possible. A linear solution should be possible. This is the method:
private static boolean BinarySearch(Comparable[] ToSearch, Comparable ToFind, int first, int last){
boolean found = false;
int mid = first + (last - first) / 2;
comparisons++;
if(first > last){
found = false;
}
else if(ToFind.compareTo(ToSearch[mid]) == 0){
found = true;
comparisons++;
}
else if(ToFind.compareTo(ToSearch[mid]) < 0) {
found = BinarySearch(ToSearch, ToFind, first, mid - 1);
comparisons++;
}
else{
found = BinarySearch(ToSearch, ToFind,mid + 1, last);
comparisons++;
}
return found;
}
The problem I am having is tracking the number of comparisons through the recursion. Because I have to count the comparisons that evaluate to false as well as true, I tried to placing the comparison incrementing statement inside each selection statement but this does not work because the statement is not incremented if the statement evaluates to false. I also cannot place them between the selection statements because that would give me else without if erros. I am wondering if it was a bad idea to use recursion at all for the search but I want to believe it is possible. Any help would be appreciated.
Perhaps you could set a variable in each if block with the number of comparisons it took to get there, and add it at the end?
private static boolean BinarySearch(Comparable[] ToSearch, Comparable ToFind, int first, int last){
boolean found = false;
int newComparisons = 0;
int mid = first + (last - first) / 2;
if(first > last){
found = false;
newComparisons = 1;
}
else if(ToFind.compareTo(ToSearch[mid]) == 0){
found = true;
newComparisons = 2;
}
else if(ToFind.compareTo(ToSearch[mid]) < 0) {
found = BinarySearch(ToSearch, ToFind, first, mid - 1);
newComparisons = 3;
}
else{
found = BinarySearch(ToSearch, ToFind,mid + 1, last);
newComparisons = 3;
}
comparisons += newComparisons;
return found;
}
Related
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
}
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;
}
Currently stuck on a problem with binary search that is asking me to pass one parameter with is an object.
But is it possible to do this?? Normally I would use two parameters for a problem like this.
Normally with binary search I use-->
int binarySearch(int[] list, int searchItem)
{
int mid=0;
int start=0;
int end=list.length-1;
boolean found=false;
//Loop until found or end of list.
while (start <= end && !found)
{
mid = (start + end) / 2;
if (list[mid] == searchItem)
found = true;
else
if (list[mid] > searchItem)
end = mid - 1;
else
start = mid + 1;
}
if(found)
return mid;
else
return(-1);
}
But is it possible to just pass in one parameter like this?? I need to search an array list.
public int binarySearch(Moon searchItem){
int mid = 0;
int start = 0;
int end = moons.size() -1;
boolean found = false;
while(start <= end && !found){
mid = (start + end) / 2;
if(moons.get(mid).equals(searchItem)){
found = true;
}
else{
if(???)) {
}
else
etc etc
}
}
return 0;
}
First you should go through this question: How to compare objects by multiple fields
Then, implement Comparable to Moon class. If you can't change Moon class, then you've to create Comparator.
You'll need to override compareTo method in Moon class which can be used in place of ??? in your question.
I have a program that creates a class dictionary, in which it populates and arrayList of strings with words given from a command line argument(in alphabetical order, all different lengths). Anyway, I need to implement binary search to look for a prefix in the dictionary as part of a backtracking method. I run into problems when the prefix is longer than the word in the dictionary---I tried to adjust binary search for this situation but it is producing incorrect results. I really don't understand binary search enough to fix this issue. If I don't account for the issue of a prefix being longer than a word, it .subString produces string indexoutofbounds. Any help would be greatly appreciated.
public int searchPrefix(String prefixKey){
int minIndex=0;
int maxIndex= newDictionary.size()-1;
return searchPrefix( prefixKey, minIndex,maxIndex);
}
public int searchPrefix(String prefixKey, int minIndex, int maxIndex){
if(minIndex>maxIndex){
return-1;
}
int midIndex=(maxIndex-minIndex)/2+minIndex;
if (prefixKey.length()>newDictionary.get(midIndex).length()){
return searchPrefix( prefixKey, midIndex+1,maxIndex);
}
else if(newDictionary.get(midIndex).length(<prefixKey.length()&&newDictionary.get(midIndex).compareTo(prefixKey.substring(0,newDictionary.get(midIndex).length()))>0){
return searchPrefix(prefixKey,minIndex,maxIndex);
}
else if(newDictionary.get(midIndex).substring(0,prefixKey.length()).compareTo(prefixKey)>0){
return searchPrefix(prefixKey,minIndex,maxIndex-1);
}
else if(newDictionary.get(midIndex).length()<prefixKey.length()&&newDictionary.get(midIndex).compareTo(prefixKey.substring(0,newDictionary.get(midIndex).length()))<0){
return searchPrefix(prefixKey,minIndex,maxIndex);
}
else if(newDictionary.get(midIndex).substring(0,prefixKey.length()).compareTo(prefixKey)<0){
return searchPrefix( prefixKey, midIndex+1,maxIndex);
}
else
return midIndex;
}
Have taken the binarySearch method from Collections class and modified as per your need.
private static int binarySearch(List<String> list, String key) {
int low = 0;
int high = list.size() - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
String midVal = list.get(mid);
int cmp = -1;
if (midVal.length() > key.length())
cmp = midVal.substring(0, key.length()).compareTo(key);
else
cmp = key.substring(0, midVal.length()).compareTo(midVal) * -1;
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -1; // key not found
}
Hope this will help you.
The String method compareTo takes care of String values of different length, so that (e.g.) "ABC" precedes "ABCD" so all of these case distinctions in your method aren't really necessary.
The cascaded of statement begins:
if (prefixKey.length() > newDictionary.get(midIndex).length()){
return searchPrefix( prefixKey, midIndex+1,maxIndex);
}
else if(newDictionary.get(midIndex).length() < prefixKey.length() && ...
But theses conditions are identical, which means that the second branch is never reached.
You have two statements:
return searchPrefix(prefixKey,minIndex,maxIndex);
Under no circumstances should a recursive call be made with exactly the same parameters as were passed to the current call: infinite recursion results.
Why can't you use Arrays.binarySearch?
I can't really suggest an improvement because you haven't described the problem. Please provide an example, giving a small dictionary and a set of keys with expected results.
I'm currently writing a binary search that uses iteration instead of recursion and its not returning anything. I've debugged it down to the while loop not ending but I can't seem to figure out where I went wrong.
Here is the code:
public static <E extends Comparable> boolean binarySearchIterative(E[] array, E obj) {
int first = 0;
int last = array.length - 1;
while(first <= last) {
int middle = (first + last) / 2;
if(array[middle].equals(obj)) return true;
else if(obj.compareTo(array[middle]) < 0) first = middle - 1;
else first = middle + 1;
}
return false;
}
And yes, my list is ordered ;)
In the second else if part you need to set last instead of first -
else if(obj.compareTo(array[middle]) < 0) last = middle - 1;