Floor in a Sorted Array - java

I have a sorted array arr[] of size n without duplicates, and given a value x. Floor of x is defined as the largest element K in arr[] such that K is smaller than or equal to x. Find the index of K(0-based indexing).
Input:
n = 7
x = 0
arr[] = {1,2,8,10,11,12,19}
Output:
1
I have to solve it in O(logn).
The code which I have below passes the test cases but it's showing time limit exceeded. What is wrong with this code?
static int findFloor(long arr[], int n, long x) {
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (arr[mid] > x) {
if (mid != 0 && arr[mid - 1] < x) {
return mid;
} else {
high = mid - 1;
}
} else if (arr[mid] < x) {
low = mid + 1;
}
}
return -1;
}

Your program will stuck in infinite for the case arr[mid] == x as you haven't handled this case. Also, I don't see this program work properly for other cases also.
For example, if I give x=7, this will return the index 2, which is wrong. The correct index should be 1 as x=7 is greater then arr[1] == 2 and less than arr[2] == 8.
Also, your code will break if I give a input of greater than the last element in the array, say x=50 in your example. You'll get an ArrayIndexOutOfBoundException as your low will increase and make the mid index out of bound.
I've corrected the aforementioned cases and the fixed function is like below:
static int findFloor(long[] arr, int n, long x) {
if (x >= arr[arr.length - 1]) {
return arr.length - 1;
}
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (arr[mid] > x) {
if (mid != 0 && arr[mid - 1] < x) {
return mid;
} else {
high = mid - 1;
}
} else {
if (arr[mid] == x || (mid != 0 && arr[mid + 1] > x)) {
return mid;
} else {
low = mid + 1;
}
}
}
return -1;
}
Hope you got your answer.

Related

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

Java: Binary Search Implementation not working using Deferred detection of equality

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.

using recursive method for trinary search

I am writing a recursive method that, instead of carrying out a binary search algorithm, splits an array into three and uses a trinary search algorithm. I am fairly positive that my recursive case is correct, yet there seems to be a problem with my base case. The base case, which is made for if the array contains two or fewer values, is supposed to check non-recursively if the value is in the array and return the index. IF the value is not found, THEN it returns -1.
For reasons I can't figure out, this method is returning -1 no matter what. Regardless of the size of the array, or whether or not the array contains the value. Here is the method.
public static int trinarySearch(int[] array, int x, int low, int high) {
if (high - low < 3) { //BASE CASE.
for (int i = low; i < high; i++) {
if (array[i] == x) {
return i;
}
}
return -1;
} else { //RECURSIVE CASE.
int firstThird = low + (high - low) / 3;
int secondThird = low + 2 * (high - low) / 3;
if (x <= array[firstThird]) {
return trinarySearch(array, x, low, firstThird - 1);
} else if (x <= array[secondThird]) {
return trinarySearch(array, x, firstThird + 1, secondThird - 1);
} else { // must be (x > array[secondThird])
return trinarySearch(array, x, secondThird + 1, high);
}
}
}
In my test code I am simply setting up an array as int[] array = {1, 2, .....}
Let's say I search for the int 2, and it is in the array. I set up an array in the test code and call the method as trinarySearch(array, 2, 0, array.length-1). It prints -1 every time. Is there something wrong with the method, or am I simply setting up my test code wrong?
You seem to be mixing up your logic for low and high. Typically, you would define the subarray under inspection to start at low (inclusive) and end at high (exclusive).
You use high inclusive (as I understand from your example call using array.length-1), but then loop like
for (int i = low; i < high; i++) {
which does not visit array[high].
The quick fix is to change < to <= and your code runs fine. However, I would recommend using the standard definition (high exclusive) because it also simplifies other parts of the code:
You don't need any of the error-prone +1 or -1 index fixes (don't forget to change <= to < in your recursive case).
high-low is the size of the subarray under inspection, so you can use high-low <= 3 which more clearly shows that your base case handles arrays up to length 3.
I think you didn't understand Heuster answer. Here is what I would have done and it seems to me that Heuster was saying the same :
public static int trinarySearch(int[] array, int x, int low, int high) {
if (high - low < 3) { //BASE CASE.
for (int i = low; i < high; i++) {
if (array[i] == x) {
return i;
}
}
return -1;
} else { //RECURSIVE CASE.
int firstThird = low + (high - low) / 3;
int secondThird = low + 2 * (high - low) / 3;
if (x < array[firstThird]) {
return trinarySearch(array, x, low, firstThird);
} else if (x < array[secondThird]) {
return trinarySearch(array, x, firstThird, secondThird);
} else { // must be (x > array[secondThird])
return trinarySearch(array, x, secondThird, high);
}
}
}
You just missed one important condition in the recursive section which is if(x==splitingIndex)
I have change your code a little and its working
See the changes
public static int trinarySearch(int[] array, int x, int low, int high) {
if (high - low < 3) {
//BASE CASE.
for (int i = low; i < high; i++) {
if (array[i] == x) {
return i;
}
}
return -1;
} else { //RECURSIVE CASE.
int firstThird = low + (high - low) / 3;
int secondThird = low + 2 * (high - low) / 3;
if(x == array[firstThird])
{
return firstThird;
}
else if (x < array[firstThird]) {
return trinarySearch(array, x, low, firstThird - 1);
}
if(x == array[secondThird])
{
return secondThird;
}
else if (x < array[secondThird]) {
return trinarySearch(array, x, firstThird + 1, secondThird - 1);
}
return trinarySearch(array, x, secondThird + 1, high);
}
}

How can i fin the index using exponential, binary or interpolatin search recursively? [duplicate]

This question already has an answer here:
How can I locate an index given the following constraints? [closed]
(1 answer)
Closed 9 years ago.
Given an array of n integers A[0…n−1], such that ∀i,0≤i≤n, we have that |A[i]−A[i+1]|≤1, and if A[0]=x, A[n−1]=y, we have that x<y. Locate the index j such that A[j]=z, for a given value of z, x≤ z ≤y
I dont understand the problem. I've been stuck on it for 4 days. Any idea of how to approach it with binary search, exponential search or interpolation search recursively? We are given an element z find the index j such that a [j] = z (a j) am i right?.
static int binarySearch(int[] searchArray, int x) {
int start, end, midPt;
start = 0;
end = searchArray.length - 1;
while (start <= end) {
midPt = (start + end) / 2;
if (searchArray[midPt] == x) {
return midPt;
} else if (searchArray[midPt] < x) {
start = midPt + 1;
} else {
end = midPt - 1;
}
}
return -1;
}
You can use the basic binary search algorithm. The fact that A[i] and A[i+1] differ by at most 1 guarantees you will find a match.
Pseudocode:
search(A, z):
start := 0
end := A.length - 1
while start < end:
x = A[start]
y = A[end]
mid := (start+end)/2
if x <= z <= A[mid]:
end := mid
else if A[mid] < z <= y
start := mid + 1
return start
Note that this doesn't necessarily return the first match, but that wasn't required.
to apply your algorithms your need a sorted array.
the condition of you problem says that you have an array which has elements that differ with max 1, not necessarily sorted!!!
so, here are the steps to write the code :
check if problem data respects given conditions
sort input array + saving old indexes values, so later can can initial positions of elements
implement you search methods in recursive way
Binary search source
Interpolation search source
Here's full example source :
public class Test {
// given start ======================================================
public int[] A = new int[] { 1, 1, 2, 3, 4, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6,
7, 8 };
public int z = 4;
// given end =======================================================
public int[] indexes = new int[A.length];
public static void main(String[] args) throws Exception {
Test test = new Test();
if (test.z < test.A[0] || test.z > test.A[test.A.length - 1]){
System.out.println("Value z="+test.z+" can't be within given array");
return;
}
sort(test.A, test.indexes);
int index = binSearch(test.A, 0, test.A.length, test.z);
if (index > -1) {
System.out.println("Binary search result index =\t"
+ test.indexes[index]);
}
index = interpolationSearch(test.A, test.z, 0, test.A.length-1);
if (index > -1) {
System.out.println("Binary search result index =\t"
+ test.indexes[index]);
}
}
public static void sort(int[] a, int[] b) {
for (int i = 0; i < a.length; i++)
b[i] = i;
boolean notSorted = true;
while (notSorted) {
notSorted = false;
for (int i = 0; i < a.length - 1; i++) {
if (a[i] > a[i + 1]) {
int aux = a[i];
a[i] = a[i + 1];
a[i + 1] = aux;
aux = b[i];
b[i] = b[i + 1];
b[i + 1] = aux;
notSorted = true;
}
}
}
}
public static int binSearch(int[] a, int imin, int imax, int key) {
// test if array is empty
if (imax < imin)
// set is empty, so return value showing not found
return -1;
else {
// calculate midpoint to cut set in half
int imid = (imin + imax) / 2;
// three-way comparison
if (a[imid] > key)
// key is in lower subset
return binSearch(a, imin, imid - 1, key);
else if (a[imid] < key)
// key is in upper subset
return binSearch(a, imid + 1, imax, key);
else
// key has been found
return imid;
}
}
public static int interpolationSearch(int[] sortedArray, int toFind, int low,
int high) {
if (sortedArray[low] == toFind)
return low;
// Returns index of toFind in sortedArray, or -1 if not found
int mid;
if (sortedArray[low] <= toFind && sortedArray[high] >= toFind) {
mid = low + ((toFind - sortedArray[low]) * (high - low))
/ (sortedArray[high] - sortedArray[low]); // out of range is
// possible here
if (sortedArray[mid] < toFind)
low = mid + 1;
else if (sortedArray[mid] > toFind)
// Repetition of the comparison code is forced by syntax
// limitations.
high = mid - 1;
else
return mid;
return interpolationSearch(sortedArray, toFind, low, high);
} else {
return -1;
}
}
}

binarySearch Method Throwing ArrayIndexOutOfBounds Exception - Java

I don't know why this method is throwing an ArrayIndexOutOfBounds exception.
When I change the initial "high" value to "int high = array.length - 1;", the program will return any integer value that I search for.
What am I doing wrong?
Thanks in advance!
public class BinarySearch {
public static void main(String[] args) {
int searchValue = 12;
int[] givenNums = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
binarySearch(givenNums, searchValue);
System.out.println("\nResult: " + searchValue);
}
public static int binarySearch(int[] array, int key) {
int low = 0;
int high = array.length;
int mid = (low + high) / 2;
int i = 0;
System.out.println();
while (low <= high) {
System.out.print(i + " ");
if (array[mid] < key) {
low = mid + 1;
mid = (low + high) / 2;
} else if (array[mid] > key) {
high = mid - 1;
mid = (low + high) / 2;
}
else
return mid;
i++;
}
return -1;
}
}
You need to be consistent about whether high means the maximum value it can be inclusively or exclusively. You start off with it being an exclusive upper bound:
int high = array.length;
But then your while loop condition is only appropriate if it's an inclusive upper bound:
while (low <= high)
You should probably just change the while condition to:
while (low < high)
... and change the assignment of high later, too.
Alternatively, you could keep it inclusive, and change the initial value to array.length - 1.
That will stop the situation where low == high == mid == array.length, which is where it would blow up.
I'd also suggest moving the mid = (low + high) / 2 computation to be the first statement within the while loop - then you can get rid of the duplicate code.
while (low < high) {
mid = (low + high) / 2;
System.out.print(i + " ");
if (array[mid] < key) {
low = mid + 1;
} else if (array[mid] > key) {
high = mid;
}
else {
return mid;
}
i++;
}
The maximum index of an array is array.length - 1 as they start from 0.
Arrays in java are indexed from 0, that means...
int[] arr = new int[10];
First value is arr[0] and last is arr[9], length is 10.

Categories