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.
Related
I'm always confused with Binary search right boarder.
For example, if we want to find the last index of target in a sorted array, the code should be:
public int binarySearchLarger(int[] nums, int target) {
int l = 0;
int r = nums.length - 1;
while (l < r) {
int m = l + (r - l + 1) / 2;
if (nums[m] < target) l = m;
else if(nums[m] > target) r = m - 1;
else l = m;
}
if (nums[l] == target) return l;
else return -1;
}
but I see lots of posts said that if we want while(l<r), then r should be nums.length, in this case, the code becomes:
public int binarySearchLarger(int[] nums, int target) {
int l = 0;
int r = nums.length;
while (l < r) {
int m = l + (r - l + 1) / 2;
if (nums[m] < target) l = m;
else if(nums[m] > target) r = m - 1;
else l = m;
}
if (nums[l] == target) return l;
else return -1;
}
but in this case, if (nums[m] < target) l = m; will throw arrayindexoutofbound exception.
My questions is: when should we use r = nums.length - 1 and when should we use r = nums.length?
See this answer for a discussion about the various options when implementing binary search: Binary Search algorithm implementations
For your problem, which seems to be to find the last instance of the target, I do it like this:
int findLastIndexOfTarget(int[] nums, int toFind) {
// find the position that divides <= toFind from > toFind
// these are the lowest and highest possible indexes
int low = 0;
int high = nums.length;
// while there is a range of possible indexes
while(low<high) {
int test = low+((high-low)/2);
if (nums[test] <= toFind) {
//too low
low = test+1; //guaranteed > low, <= high
} else {
//not too low
high = test; //guaranteed >= low, < high
}
}
// guaranteed low == high
// We found the position we were looking for. See if the target is on the left
return (low > 0 && nums[low-1]==target) ? low-1 : -1;
}
I do it like this:
int findEndIndex( vector<int> &nums, int target ){
int l = 0, r = nums.size() - 1;
while( l < r ){
int m = (l + (r - l) / 2) + 1;
if( nums[m] == target )
l = m;
else if( nums[m] < target )
l = m + 1;
else
r = m - 1;
}
return l >= nums.size() || nums[l] != target ? -1 : l;
}
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;
}
}
I came across a interview question that has to be done in O(logn)
Given a sorted integer array and a number, find the start and end indexes of the number in the array.
Ex1: Array = {0,0,2,3,3,3,3,4,7,7,9} and Number = 3 --> Output = {3,6}
Ex2: Array = {0,0,2,3,3,3,3,4,7,7,9} and Number = 5 --> Output = {-1,-1}
I am trying to find an efficient algo for this but so fat have not been successful.
You can use the concept of binary search to find the starting and ending index:
To find the starting index, you halve the array, if the value is equal to or greater than the input number, repeat with the lower half of the array, otherwise repeat with the higher half. stop when you reached an array of size 1.
To find the starting index, you halve the array, if the value is greater than the input number, repeat with the lower half of the array, otherwise repeat with the higher half. stop when you reached an array of size 1.
Note that when we reached an array of size 1, we may be one cell next to the input number, so we check if it equals the input number, if not, we fix the index by adding/decreasing 1 from the index we found.
findStartIndex(int[] A, int num)
{
int start = 0; end = A.length-1;
while (end != start)
{
mid = (end - start)/2;
if (A[mid] >= num)
end = mid;
else
start = mid;
}
if(A[start] == num)
return start;
else
return start+1;
}
findEndIndex(int[] A, int num)
{
int start = 0; end = A.length-1;
while (end != start)
{
mid = (end - start)/2;
if (A[mid] > num)
end = mid;
else
start = mid;
}
if(A[start] == num)
return start;
else
return start-1;
}
And the whole procedure:
int start = findStartIndex(A, num);
if (A[start]!=num)
{
print("-1,-1");
}
else
{
int end = findEndIndex(A, num);
print(start, end);
}
Sounds like a binary search -- log graphs iirc represent the effect of "halving" with each increment, which basically is binary search.
Pseudocode:
Set number to search for
Get length of array, check if number is at the half point
if the half is > the #, check the half of the bottom half. is <, do the inverse
repeat
if the half point is the #, mark the first time this happens as a variable storing its index
then repeat binary searches above , and then binary searches below (separately), such that you check for how far to the left/right it can repeat.
note*: and you sort binary left/right instead of just incrementally, in case your code is tested in a dataset with like 1,000,000 3's in a row or something
Is this clear enough to go from there?
The solution is to binary search the array concurrently (does't actually have to be concurrent :P ) at the start. The key is that the left and right searches are slightly different. For the right side if you encounter a dupe you have to search to the right, and for the left side if you encounter a dupe you search to the left. what you are searching for is the boundary so on the right side you check for.
yournum, not_yournum
This is the boundary and on the left side you just search for the boundary in the opposite direction. At the end return the indices of the boundaries.
Double binary search. You start with lower index = 0, upper index = length - 1. Then you check the point halfway and adjust your indexes accordingly.
The trick is that once you've found target, the pivot splits in two pivots.
Since no one has posted working code yet, I'll post some (Java):
public class DuplicateNumberRangeFinder {
public static void main(String[] args) {
int[] nums = { 0, 0, 2, 3, 3, 3, 3, 4, 7, 7, 9 };
Range range = findDuplicateNumberRange(nums, 3);
System.out.println(range);
}
public static Range findDuplicateNumberRange(int[] nums, int toFind) {
Range notFound = new Range(-1, -1);
if (nums == null || nums.length == 0) {
return notFound;
}
int startIndex = notFound.startIndex;
int endIndex = notFound.endIndex;
int n = nums.length;
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (nums[mid] == toFind && (mid == 0 || nums[mid - 1] < toFind)) {
startIndex = mid;
break;
} else if (nums[mid] < toFind) {
low = mid + 1;
} else if (nums[mid] >= toFind) {
high = mid - 1;
}
}
low = 0;
high = n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (nums[mid] == toFind && (mid == n - 1 || nums[mid + 1] > toFind)) {
endIndex = mid;
break;
} else if (nums[mid] <= toFind) {
low = mid + 1;
} else if (nums[mid] > toFind) {
high = mid - 1;
}
}
return new Range(startIndex, endIndex);
}
private static class Range {
int startIndex;
int endIndex;
public Range(int startIndex, int endIndex) {
this.startIndex = startIndex;
this.endIndex = endIndex;
}
public String toString() {
return "[" + this.startIndex + ", " + this.endIndex + "]";
}
}
}
It may be error on my end, but Ron Teller's answer has an infinite loop when I've tested it. Here's a working example in Java, that can be tested here if you change the searchRange function to not be static.
import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;
public class RangeInArray {
// DO NOT MODIFY THE LIST
public static ArrayList<Integer> searchRange(final List<Integer> a, int b) {
ArrayList<Integer> range = new ArrayList<>();
int startIndex = findStartIndex(a, b);
if(a.get(startIndex) != b) {
range.add(-1);
range.add(-1);
return range;
}
range.add(startIndex);
range.add(findEndIndex(a, b));
return range;
}
public static int findStartIndex(List<Integer> a, int b) {
int midIndex = 0, lowerBound = 0, upperBound = a.size() - 1;
while(lowerBound < upperBound) {
midIndex = (upperBound + lowerBound) / 2;
if(b <= a.get(midIndex)) upperBound = midIndex - 1;
else lowerBound = midIndex + 1;
}
if(a.get(lowerBound) == b) return lowerBound;
return lowerBound + 1;
}
public static int findEndIndex(List<Integer> a, int b) {
int midIndex = 0, lowerBound = 0, upperBound = a.size() - 1;
while(lowerBound < upperBound) {
midIndex = (upperBound + lowerBound) / 2;
if(b < a.get(midIndex)) upperBound = midIndex - 1;
else lowerBound = midIndex + 1;
}
if(a.get(lowerBound) == b) return lowerBound;
return lowerBound - 1;
}
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(1);
list.add(2);
list.add(2);
list.add(2);
list.add(2);
list.add(2);
list.add(2);
list.add(3);
list.add(4);
list.add(4);
list.add(4);
list.add(4);
list.add(5);
list.add(5);
list.add(5);
System.out.println("Calling search range");
for(int n : searchRange(list, 2)) {
System.out.print(n + " ");
}
}
}
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;
}
}
}
Say you have a sorted array of integers:
{3,4,4,6,10,15,15,19,23,23,24,30}
And you want to find the number of integers that fall within a range of 4 and 23.
{4,4,6,10,15,15,19,23,23}
Thus the result would be 9.
I wrote a binarysearch implementation, but I'm not sure how I would modify it to also take into account the fact that there can be multiple integers that match the upper bounds of the range.
I thought of adding a boolean in the method signature to ask whether to look for the upper bounds of the key, but I'm not sure if it can be done in a single method while keeping O(log(N)) complexity.
Or is there some other way of finding the # of items in that range in the sorted array in O(log(N)) time?
This is what I have so far:
int start = rangeBinarySearch(arr, 4, false);
int end = rangeBinarySearch(arr, 23, true); // true would indicate that I want the position of the last occurrence of the key.
int totalInRange = (Math.abs(end) - Math.abs(start) -1)
private static int rangeBinarySearch(int[] items, int key, boolean lastIndex) {
if(items == null)
throw new IllegalArgumentException();
int start = 0;
int end = items.length - 1;
while(start <= end) {
int mIndex = (start + end) / 2;
int middle = items[mIndex];
if(middle < key)
start = (mIndex +1);
else if(middle > key)
end = (mIndex -1);
else
return mIndex; // Possible something here to find the upper bounds?
}
return -(start +1);
}
Range binary search for the lower bound and the upper bound are different. Here different means they have different stopping criteria and return step.
For the lower bound (left range), you can call the following function to get the index in the sorted array where the value is larger or equal than it, -1 otherwise.
int binarySearchForLeftRange(int a[], int length, int left_range)
{
if (a[length-1] < left_range)
return -1;
int low = 0;
int high = length-1;
while (low<=high)
{
int mid = low+((high-low)/2);
if(a[mid] >= left_range)
high = mid-1;
else //if(a[mid]<i)
low = mid+1;
}
return high+1;
}
For the upper bound (right range), you can call the following function to get the index in the sorted array where the value is smaller or equal than it, -1 otherwise.
int binarySearchForRightRange(int a[], int length, int right_range)
{
if (a[0] > right_range)
return -1;
int low = 0;
int high = length-1;
while (low<=high)
{
int mid = low+((high-low)/2);
if(a[mid] > right_range)
high = mid-1;
else //if(a[mid]<i)
low = mid+1;
}
return low-1;
}
Finally, if you want to get the number of how many elements within this range, it's easy based on return values of these two above functions.
int index_left = binarySearchForLeftRange(a, length, left_range);
int index_right = binarySearchForRightRange(a, length, right_range);
if (index_left==-1 || index_right==-1 || index_left>index_right)
count = 0;
else
count = index_right-index_left+1;
Test: (with duplicates)
int a[] = {3,4,4,6,10,15,15,19,23,23,24,30};
int length = sizeof(arr)/sizeof(arr[0]);
int left_range = 4;
int right_range = 23;
int index_left = binarySearchForLeftRange(a, length, left_range); // will be 1
int index_right = binarySearchForRightRange(a, length, right_range); // will be 9
int count; // will be 9
if (index_left==-1 || index_right==-1 || index_left>index_right)
count = 0;
else
count = index_right-index_left+1;
EDIT: Of course, you can merge the first two functions into one by passing one extra flag to indicate it as lower bound or upper bound, though it will be much more clear if not. Your choice!
If you are not learning the algorithm use standart functions instead:
Arrays.binarySearch
You basicly need first occurence of your first element (4) and last occurence of last (23) and substract. But there is no need for (4) to be there, so read documentation of Arrays.binarySearch, it gives you where (4) would be.
If you are expecting lots of (4)s you have to write your own binSearch, that returns both first and last index:
find first occurence at index i
if there is previous one look at i/2, if there is (4) look at i/4 else look at 3*i/4
...
You need to perform two binary searches to find the lowest index before the rangeLow and the highestIndex after rangeHigh, that way you can count duplicates within the range.
This would give a time complexity of o(2 log n) as we perform the binary search twice.
private int searchArrayForNumbersInRange(int[] arr, int start, int end) {
int leftIndex = searchLeft(arr, start);
int rightIndex = searchRight(arr, end);
int count;
if (leftIndex < 0 || rightIndex < 0)
return -1;
if (rightIndex == leftIndex)
count = 1;
else {
count = rightIndex - leftIndex;
}
return count;
}
private int searchLeft(int[] arr, int start) {
int lo = 0;
int hi = arr.length - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (arr[mid] == start && arr[mid -1] < start) {
return mid - 1;
}
if (arr[mid] >= start)
hi = mid - 1;
else
lo = mid + 1;
}
return -1;
}
private int searchRight(int[] arr, int end) {
int lo = 0;
int hi = arr.length -1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (arr[mid] == end && arr[mid+1] > end)
return mid;
if (mid <= end)
lo = mid + 1;
else
hi = mid - 1;
}
return -1;
}