I need help with implementing the binary search algorithm, can someone tell me what's wrong with my code:
public int bsearch(Item idToSearch) {
int lowerBoundary = 0;
int upperBoundary = myStore.size() - 1;
int mid = -1;
while(upperBoundary >= lowerBoundary) {
mid = (lowerBoundary + upperBoundary) / 2;
//if element at middle is less than item to be searched, than set new lower boundary to mid
if(myStore.get(mid).compareTo(idToSearch) < 0) {
lowerBoundary = mid - 1;
} else {
upperBoundary = mid + 1;
}
} //end while loop
if(myStore.get(mid).equals(idToSearch)) {
return mid;
} else {
return -1; // item not found
}
} // end method
I think you made a mistake when update lowerBoundary and upperBoundary.
It may be:
if(myStore.get(mid).compareTo(idToSearch) < 0){
lowerBoundary = mid + 1;
} else {
upperBoundary = mid - 1;
}
And why don't you break the loop if you find the element at mid?
I would
stop when the lower and upper bound are the same.
stop when you find a match
use mid = (hi + lo) >>> 1; to avoid an overflow causing a bug.
read the code in the JDK which does this already as it works correctly.
The first issue is with the boundaries, also you should stop in case he found the value, but he doesn't which lead to possible overlook.
while(upperBoundary >= lowerBoundary)
{
mid = (lowerBoundary + upperBoundary) / 2;
if (myStore.get(mid).equals(idToSearch)) break; // goal
if(myStore.get(mid).compareTo(idToSearch) < 0)
{
lowerBoundary = mid + 1;
}
else
{
upperBoundary = mid - 1;
}
}
Related
The objective is to return the index of an element in a string array if present. The method uses a basic binary search using compareTo statements. With more than two elements in a tested array, the method will not detect the present element and return -1. The Java code this question is referring to is below. How do I make the binary search method work as intended?
public static int binarySearch(String[] array, String x) {
int high = array.length - 1;
int low = 0;
while (low <= high) {
int mid = low + (high - low) / 2;
if (x.compareTo(array[mid]) == 0) {
return mid;
}
if (x.compareTo(array[mid]) > 0) {
low = mid + 1;
}
else {
high = mid - 1;
}
}
return -1;
}
add an extra variable and set it to -1;
int loc=-1;
change the code
int mid=low+(high-low)/2;
to
int mid=(low+high)/2;
if(x.compareTo(array[mid]==0)
{
loc=mid;
break;
}
else if(x<array[mid])
{
last=mid-1;
}
else
{
low=mid+1;
}
then
if(loc>=0)
{
System.out.println(loc+1);
}
else
{
System.out.println("no element");
}
I just had to pop in here to hopefully get a quick answer to my little problem. I'm trying to create a binary search method but ran into some problems. I created it iteratively, which my IDE(Intellij) apparently didn't like. It had me stuck in a endless loop of... well, you know the rest. Any suggestions?
Here is my simple, yet beautiful little snippet of code:
static int searchIt(int[] arr, int target){
int left = 0;
int right = arr.length - 1;
while (left <= right){
int mid = (right + left) / 2;
if(arr[mid] == target) {
return mid;
} else if(target < arr[mid]){
right = mid-1;
} else{
left = mid -1;
}
}
return -1;
}
No errors, not in runtime or compile time, just endless nothingness...
left value should be mid + 1 since if the your middle your target value greater than your mid value then you need to search the element in second half of your array
static int searchIt(int[] arr, int target){
int left = 0;
int right = arr.length - 1;
while (left <= right){
int mid = (right + left) / 2;
if(arr[mid] == target) {
return mid;
} else if(target < arr[mid]){
right = mid - 1;
} else{
left = mid + 1;
}
}
return -1;
}
I am attempting to expand a function to find the number of integer matches through Binary Search by resetting the high variable, but it gets stuck in a loop. I am guessing a workaround would be to duplicate this function to obtain the last index to determine the number of matches, but I do not think this would be such an elegant solution.
From this:
public static Matches findMatches(int[] values, int query) {
int firstMatchIndex = -1;
int lastMatchIndex = -1;
int numberOfMatches = 0;
int low = 0;
int mid = 0;
int high = values[values.length - 1];
boolean searchFirst = false;
while (low <= high){
mid = (low + high)/2;
if (values[mid] == query && firstMatchIndex == -1){
firstMatchIndex = mid;
if (searchFirst){
high = mid - 1;
searchFirst = false;
} else {
low = mid + 1;
}
} else if (query < values[mid]){
high = mid - 1;
} else {
low = mid + 1;
}
}
if (firstMatchIndex != -1) { // First match index is set
return new Matches(firstMatchIndex, numberOfMatches);
}
else { // First match index is not set
return new Matches(-1, 0);
}
}
To something like this:
public static Matches findMatches(int[] values, int query) {
int firstMatchIndex = -1;
int lastMatchIndex = -1;
int numberOfMatches = 0;
int low = 0;
int mid = 0;
int high = values[values.length - 1];
boolean searchFirst = false;
while (low <= high){
mid = (low + high)/2;
if (values[mid] == query && firstMatchIndex == -1){
firstMatchIndex = mid;
if (searchFirst){
high = values[values.length - 1]; // This is stuck in a loop
searchFirst = false;
}
} else if (values[mid] == query && lastMatchIndex == -1){
lastMatchIndex = mid;
if (!searchFirst){
high = mid - 1;
} else {
low = mid + 1;
}
} else if (query < values[mid]){
high = mid - 1;
} else {
low = mid + 1;
}
}
if (firstMatchIndex != -1) { // First match index is set
return new Matches(firstMatchIndex, numberOfMatches);
}
else { // First match index is not set
return new Matches(-1, 0);
}
}
There is a problem with your code:
high = values[values.length - 1];
should be
high = values.length - 1;
Also you do not need variables like numberOfMatches and searchFirst, we can have rather simple solution.
Now coming to the problem,I understand what you want I think Binary Search is appropriate for such query.
The Best way to do the required is once a match is found you just go forward and backward from that index until a mismatch occurs and this would be both elegant and efficient in calculating the firstMatchIndex and numberOfMatches.
So your function should be:
public static Matches findMatches(int[] values, int query)
{
int firstMatchIndex = -1,lastMatchIndex=-1;
int low = 0,mid = 0,high = values.length - 1;
while (low <= high)
{
mid = (low + high)/2;
if(values[mid]==query)
{
lastMatchIndex=mid;
firstMatchIndex=mid;
while(lastMatchIndex+1<values.length&&values[lastMatchIndex+1]==query)
lastMatchIndex++;
while(firstMatchIndex-1>=0&&values[firstMatchIndex-1]==query)
firstMatchIndex--;
return new Matches(firstMatchIndex,lastMatchIndex-firstMatchIndex+1);
}
else if(values[mid]>query)
high=mid-1;
else low=mid+1;
}
return new Matches(-1,0);
}
Couldn't you just use something like a set to find duplicates?
Something like this:
package example;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
public class DuplicatesExample {
public static void main(String[] args) {
String[] strings = { "one", "two", "two", "three", "four", "five", "six", "six" };
List<String> dups = getDups(strings);
System.out.println("DUPLICATES:");
for(String str : dups) {
System.out.println("\t" + str);
}
}
private static List<String> getDups(String[] strings) {
ArrayList<String> rtn = new ArrayList<String>();
HashSet<String> set = new HashSet<>();
for (String str : strings) {
boolean added = set.add(str);
if (added == false ) {
rtn.add(str);
}
}
return rtn;
}
}
Output:
DUPLICATES:
two
six
I have split your problems into two parts - using binary search to find a number and counting the number of matches. The first part is resolved by the search function while the second part is resolved by the findMatches function:
public static Matches findMatches(int[] values, int query) {
int leftIndex = -1;
int rightIndex = -1;
int high = values.length - 1;
int matchedIndex = search(values, 0, high, query);
//if at least one match
if (matchedIndex != -1) {
//decrement upper bound of left array
int leftHigh = matchedIndex - 1;
//increment lower bound of right array
int rightLow = matchedIndex + 1;
//loop until no more duplicates in left array
while (true) {
int leftMatchedIndex = search(values, 0, leftHigh, query);
//if duplicate found
if (leftMatchedIndex != -1) {
leftIndex = leftMatchedIndex;
//decrement upper bound of left array
leftHigh = leftMatchedIndex - 1;
} else {
break;
}
}
//loop until no more duplicates in right array
while(true){
int rightMatchedIndex = search(values, rightLow, high, query);
//if duplicate found
if(rightMatchedIndex != -1){
rightIndex = rightMatchedIndex;
//increment lower bound of right array
rightLow = rightMatchedIndex + 1;
} else{
break;
}
}
return new Matches(matchedIndex, rightIndex - leftIndex + 1);
}
return new Matches(-1, 0);
}
private static int search(int[] values, int low, int high, int query) {
while (low <= high) {
int mid = (low + high) / 2;
if (values[mid] == query) {
return mid;
} else if (query < values[mid]) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
I found a solution after correcting a mistake with resetting high variable that caused an infinite loop.
public static Matches findMatches(int[] values, int query) {
int firstMatchIndex = -1;
int lastMatchIndex = -1;
int numberOfMatches = 0;
int low = 0;
int mid = 0;
int high = values.length - 1;
while (low <= high){
mid = (low + high)/2;
if (values[mid] == query && firstMatchIndex == -1){
firstMatchIndex = mid;
numberOfMatches++;
high = values.length - 1;
low = mid;
} else if (values[mid] == query && (lastMatchIndex == -1 || lastMatchIndex != -1)){
lastMatchIndex = mid;
numberOfMatches++;
if (query < values[mid]){
high = mid - 1;
} else {
low = mid + 1;
}
} else if (query < values[mid]){
high = mid - 1;
} else {
low = mid + 1;
}
}
if (firstMatchIndex != -1) { // First match index is set
return new Matches(firstMatchIndex, numberOfMatches);
}
else { // First match index is not set
return new Matches(-1, 0);
}
}
Not having any knowledge of the data other than sorted a priori is tough.
See this:
Binary Search O(log n) algorithm to find duplicate in sequential list?
This will find the first index of duplicates of k in a sorted array.
Of course this is related to knowing the value of duplicate first but very useful when it is known.
public static int searchFirstIndexOfK(int[] A, int k) {
int left = 0, right = A.length - 1, result = -1;
// [left : right] is the candidate set.
while (left <= right) {
int mid = left + ((right - left) >>> 1); // left + right >>> 1;
if (A[mid] > k) {
right = mid - 1;
} else if (A[mid] == k) {
result = mid;
right = mid - 1; // Nothing to the right of mid can be
// solution.
} else { // A[mid] < k
left = mid + 1;
}
}
return result;
}
This will find a dupe in log(n) time but is fragile in that the data must be sorted as well as increasing by 1 and in the range 1..n.
static int findeDupe(int[] array) {
int low = 0;
int high = array.length - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
if (array[mid] == mid) {
low = mid + 1;
} else {
high = mid - 1;
}
}
System.out.println("returning" + high);
return high;
}
I am trying to implement Binary Search Java version. From wikipedia http://en.wikipedia.org/wiki/Binary_search_algorithm#Deferred_detection_of_equality I noticed that deferred detection of equality version. It's working using that algorithm. However, when I was trying to change the if condition expression like this:
public int bsearch1(int[] numbers, int key, int start){
int L = start, R = numbers.length - 1;
while(L < R){
//find the mid point value
int mid = (L + R) / 2;
if (numbers[mid] >key){
//move to left
R = mid - 1;
} else{
// move to right, here numbers[mid] <= key
L = mid;
}
}
return (L == R && numbers[L] == key) ? L : -1;
}
It's not working properly, which goes into an infinity loop. Do you guys have any ideas about it? Thank you so much.
You've missed the effect of the assert in the Wiki you link to.
It states:
code must guarantee the interval is reduced at each iteration
You must exit if your mid >= R.
Added
The Wiki is actually a little misleading as it suggests that merely ensuring mid < r is sufficient - it is not. You must also guard against mid == min (say you have a 4 entry array and l = 2 and r = 3, mid would become 2 and stick there because 2 + 3 = 5 and 5 / 2 = 2 in integer maths).
The solution is to round up after the / 2 which can be easily achieved by:
int mid = (l + r + 1) / 2;
The final corrected and tidied code goes a little like this:
public int binarySearch(int[] numbers, int key, int start) {
int l = start, r = numbers.length - 1;
while (l < r) {
//find the mid point value
int mid = (l + r + 1) / 2;
if (numbers[mid] > key) {
//move to left
r = mid - 1;
} else {
// move to right, here numbers[mid] <= key
l = mid;
}
}
return (l == r && numbers[l] == key) ? l : -1;
}
public void test() {
int[] numbers = new int[]{1, 2, 5, 6};
for (int i = 0; i < 9; i++) {
System.out.println("Searching for " + i);
System.out.println("Found at " + binarySearch(numbers, i, 0));
}
}
There is a trivially similar algorithm here that suggests the correct approach looks more like:
public int binarySearch(int[] numbers, int key) {
int low = 0, high = numbers.length;
while (low < high) {
int mid = (low + high) / 2;
if (numbers[mid] < key) {
low = mid + 1;
} else {
high = mid;
}
}
return low < numbers.length && numbers[low] == key ? low : -1;
}
This takes a slightly different approach to the boundary conditions where high = max + 1 and also works perfectly.
I have a list of students, and i would like to sort them by the last name.
The student list looks a bit like this:
Amanda
Dorris
Tucker
Yasmin
Zara
I would like to use the binary search approach to search through these students and output the desired result.
This is what i have so far:
public void binarySearch(String keyword) {
int output;
if (fileSorted == false) {
System.out.println("The file " + fileName + " is not sorted. Please wait while it gets sorted...");
bubbleSort();
System.out.println("Thank you for your patience.");
System.out.println();
System.out.print("Search for: ");
keyword = elmo.nextLine();
output = doBinarySearch(keyword);
} else {
output = doBinarySearch(keyword);
}
System.out.println(output);
}
public int doBinarySearch(String keyword) {
int start = 0;
int end = numStudents - 1;
int mid;
int result;
while (start < end) {
mid = start + (end - start) / 2;
result = students[mid].returnLastName().compareToIgnoreCase(keyword);
if (result == 0) {
return mid;
} else if ((end - start) <= 1 ) {
return -1;
} else if (result > 0) {
start = mid;
} else if (result < 0) {
end = mid;
}
}
return -1;
}
The line
mid = ((end - start) / 2);
is wrong. You need to set mid to (roughly) the midpoint of start and end, so
mid = start + (end - start) / 2;
or
mid = (end + start) / 2;
if you're not afraid of overflow.
With what you have, mid is always in the first half of the array.
Also, you have your cases
} else if (result > 0) {
start = mid;
} else if (result < 0) {
end = mid;
}
wrong.
result = students[mid].returnLastName().compareToIgnoreCase(keyword);
returns a positive number when the last name of students[mid] is lexicographically greater than keyword, so then you need to change end, not start.
Instead of using inequality in your loop condition -- while (start != end) -- use while (start < end). This is the typical approach. When you test for equality, you make the assumption that start and end only change by one in each iteration, and that may not necessarily be true.