Why is my Binary search stuck in an endless loop? - java

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;
}

Related

Finding first and last occurrence of an element using BinarySearch

I can find the first occurrence efficiently but I'm having trouble finding the last occurrence. The answer I get for the last occurrence of the element is always the correct index plus one.
Here is my code:
class FirstAndLastOccurence{
public static int first(int[] arr, int n, int x){
int left = 0;
int right = n-1;
int res = -1;
while(left <= right){
int mid = left + (right-left)/2;
if(x < arr[mid])
right = mid-1;
if(x > arr[mid])
left = mid+1;
else{
res = mid;
right = mid-1;
}
}
return res;
}
public static int last(int[] arr, int n, int x){
int left = 0;
int right = n-1;
int res = -1;
while(left <= right){
int mid = left + (right-left)/2;
if(x < arr[mid])
right = mid-1;
if(x > arr[mid])
left = mid+1;
else{
res = mid;
left = mid+1;
}
}
return res;
}
public static void main(String[] args){
int[] arr = new int[]{2, 4, 10, 10, 10, 10, 56, 71, 90};
System.out.println(first(arr, arr.length, 10));
System.out.println(last(arr, arr.length, 10));
}
}
Output:
2
6
The first output is correct while my last output is wrong, as it should have been 5. The code I'm trying to follow is here (the code is not exactly the same, I'm implementing the binary search in a more familiar way to me personally).
You have added both the if statements and else statement at the last which gets executed only for the second if statement which is incorrect. Please find the below code which is working and giving the correct answer.
public static int last(int[] arr, int n, int x){
int left = 0;
int right = n-1;
int res = -1;
while(left <= right){
int mid = left + (right-left)/2;
if(x < arr[mid])
right = mid-1;
else if(x > arr[mid])
left = mid+1;
else{
res = mid;
left = mid+1;
}
}
return res;
}

Binary Search Specifics

I am hoping someone can help me understand this code for binary search. This is from LeetCode, it is "Template 2" under Binary Search.
I was wondering why you set right to nums.length
-Is there a reason why it isn't nums.length - 1 ?
int binarySearch(int[] nums, int target){
if(nums == null || nums.length == 0)
return -1;
int left = 0, right = nums.length;
while(left < right){
// Prevent (left + right) overflow
int mid = left + (right - left) / 2;
if(nums[mid] == target){ return mid; }
else if(nums[mid] < target) { left = mid + 1; }
else { right = mid; }
}
// Post-processing:
// End Condition: left == right
if(left != nums.length && nums[left] == target) return left;
return -1;
}

My binary search code is too slow

i am trying to solve this algorithm task. And when i submit my code, on some test cases my code is too slow and on some my code gives wrong output. I was trying to find where i made mistake but i really couldnt. Because in test cases where my code fails there are more thousand length arrays and i cant check every output to find mistake.
So i was wondering if you could give me some advice:
What can i do to improve my algorithm efficiency.
Where i make mistake so on some test cases i get wrong output.
Here is my code:
public class Main
{
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
int length = sc.nextInt();
int arr[] = new int[length];
for(int i=0; i<length; i++)
arr[i] = sc.nextInt();
int test = sc.nextInt();
int type, check;
for(int i=0; i<test; i++)
{
type = sc.nextInt();
check = sc.nextInt();
if(type == 0)
{
System.out.println(greaterOrEqualThan(arr, check, length));
}
else if(type == 1)
{
System.out.println(greaterThan(arr, check, length));
}
}
}
public static int greaterThan(int arr[],int x, int length)
{
int low = 0;
int high = length-1;
int mid;
while( low+1 < high)
{
mid = (high+low)/2;
if(arr[mid] <= x)
low = mid;
else if(arr[mid] > x)
high = mid;
}
int startIndex;
if(arr[low] > x && arr[low] != arr[high])
startIndex = low;
else if(arr[high] > x && arr[high] != arr[low])
startIndex = high;
else
return 0;
return length-startIndex;
}
public static int greaterOrEqualThan(int arr[], int x, int length)
{
int low = 0;
int high = length-1;
int mid;
while(low+1 < high)
{
mid = (low+high)/2;
if(arr[mid] < x)
low = mid;
else if(arr[mid] == x)
{
high = mid;
break;
}
else
high = mid;
}
int startIndex;
if(arr[low] >= x)
startIndex = low;
else if(arr[high] >= x)
startIndex = high;
else
return 0;
return length-(startIndex);
}
}
I think one or both of your algorithms may be incorrect in cases where there are multiple instances of the target value in the array. (e.g. [1,3,3,3,5].
Three Cases to Consider
There are three cases to consider:
x does not occur in the array
x occurs in the array exactly once
x occurs in the array more than once
How To Solve
I recommend using a classical binary search algorithm for each of the two methods (the exact binary search algorithm without modification). What you do after that is what is different.
So first, run a classical binary search algorithm, inlined directly into your methods (so that you have access to the terminal values of low and high).
Second, after the binary search terminates, test if array[mid] != x. If array[mid] != x, then x does not occur in the array and it is true that low == high + 1 (since high and low have crossed. Therefore, the count of numbers in the array which are not less than x and the count of numbers in the array which are greater than x are both equal to array.length - low.
Third, if it is instead true that array[mid] == x, then x does occur one or more times in the array. Since the classical binary search algorithm terminates immediately when if finds x, it is indeterminate "which" x it terminated on.
In this case, in order to find the count of numbers not less than x, you must find the "first" x in the array using the following code snippet:
do {
mid = mid - 1;
} while (array[mid] == x);
mid will then be the index of the element immediately before the "first" x in the array, and so the count of numbers not less than x will be array.length - mid + 1.
Similarly, in order to find the count of numbers greater than x, you must first find the "last" x in the array using the following code snippet:
do {
mid = mid + 1;
} while (array[mid] == x);
mid will then be the index of the element immediately after the "last" x in the array, and so the count of numbers greater than x will be array.length - mid - 1.
Code
simplified, inlined version of a classical binary search
int low = 0;
int high = array.length - 1;
int mid = (high + low) / 2; // priming read
while (array[mid] != x && low <= high) {
if (array[mid] > x)
high = mid - 1;
else // (array[mid] < x)
low = mid + 1;
mid = (high - mid) / 2;
}
not less than x
int countNotLessThan(int[] array, int x)
{
/* simplified, inlined classical binary search goes here */
if (array[mid] != x) {
return array.length - low;
}
else { // array[mid] == x
do {
mid = mid - 1;
} while (array[mid] == x);
return array.length - mid + 1;
}
}
greater than x
int countGreaterThan(int[] array, int x)
{
/* simplified, inlined classical binary search goes here */
if (array[mid] != x) {
return array.length - low;
}
else { // array[mid] == x
do {
mid = mid + 1;
} while (array[mid] == x);
return array.length - mid - 1;
}
}

Median of Medians algorithm error

I'm implementing a select-kth algorithm using the Median of Medians pivot method. Specifically, I'm following the pseudocode listed here.. However, my code crashes (error discussed below), I see why it crashes, but I don't understand what I can do about it.
The Error
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 32017219
at Selection.partition(Selection.java:173)
The Reason
In the wiki link, the method select will return a value if left == right. However, when the mutual recursion is called from the return statement of pivot, that means it will return a value (and not an index) back to the parent and the parent will set that value as the pivotIndex.
The Code
public static int alg5(int[] a, int left, int right, int k) {
if (left == right) {
return a[left];
}
while (true) {
int pivotIndex = pivot(a, left, right);
pivotIndex = partition(a, left, right, pivotIndex);
if (k == pivotIndex) {
return a[k];
} else if (k < pivotIndex) {
right = pivotIndex - 1;
} else {
left = pivotIndex + 1;
}
}
}
public static int pivot(int[] a, int left, int right) {
for (int i = left; i <= right; i = i + 5) {
int subRight = i + 4;
if (subRight > right) {
subRight = right;
}
int median5 = partition5(a, i, subRight);
swap(a, median5, (int)(Math.floor(i / 5)));
}
return alg5(a, left, (int)(left + Math.ceil((right - left) / 5) - 1), ((right - left) / 10));
}
public static int partition5(int[] a, int left, int right) {
Arrays.sort(a);
return ((a.length - 1) / 2);
}
public static int partition(int[] a, int left, int right, int pivotIndex) {
int pivotValue = a[pivotIndex];
a = swap(a, pivotIndex, right);
int storeIndex = left;
for (int i = left; i <= right; i++) {
int aOfi = a[i];
if (a[i] < pivotValue) {
swap(a, storeIndex, i);
storeIndex++;
}
}
swap(a, right, storeIndex);
return storeIndex;
}
I completely understand why my code isn't working, I just don't understand how to fix it since it looks to be exactly what the algorithm specifies. Pointers are greatly appreciated!
There are quite a few mistakes:
The method pivot should not modify the array a. It should just find a pivot for the future partition. A dirty fix is to call pivot(a.clone(), left, right);. (You shouldn't do this, just to give you an idea.)
Math.ceil((right - left) / 5) is an integer division. You should cast them to floats: Math.ceil(((float)(right - left)) / 5f).
In partition5, you sort the whole array a! You should just do comparisons to find the index of the median between a[left] and a[right].
At some point you might have right < left, so the first line of alg5 you should write if (left >= right)

Binary search implementation java algorithm

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;
}
}

Categories