Number of Comparisons Made by a Binary Search - java

Is it possible to count the number of comparisons made by a recursive binary search? If so, how?
Here is the search I am referring to:
//binary search
public static int binarySearch(int[] items, int start, int end, int goal)
{
if (start > end)
return (-1);
else
{
int mid = (start + end)/2;
if (goal == items[mid])
return (mid);
else
if (goal < items[mid])
return (binarySearch(items, start, mid - 1, goal));
else
return (binarySearch(items, mid + 1, end, goal));
}
}//end binarySearch

Declare your count variable outside of the method. Then add 1 each time that you call the method.
long count = 0;
//binary search
public static int binarySearch(int[] items, int start, int end, int goal)
{
count += 1
if (start > end)
return (-1);
else
{
int mid = (start + end)/2;
if (goal == items[mid])
return (mid);
else
if (goal < items[mid])
return (binarySearch(items, start, mid - 1, goal));
else
return (binarySearch(items, mid + 1, end, goal));
}
}//end binarySearch

Your most likely approach if you want to caller to be able to access the count is to pass in an accumulator into your binary search. In java 7 or less, AtomicLong may be a good choice (though it does have some overhead). In java 8, a LongAdder would also be good:
public static int binarySearch(int[] items, int start, int end, int goal, AtomicLong counter) {
}
and each time you do a comparison, you would just increment the count:
counter.incrementAndGet()
When you exit the search, the counter will contain the number of comparisons:
long count = counter.get()

Related

Recursion stackoverflow Java

I need to count the number of numbers on the right that are less than the number arr[i]. My problem is that the stack overflows at large sizes and I can't solve it in any way. Please tell me how can I refactor my code to avoid the error StackOverflow ?
public class Smaller {
public static int[] smaller(int[] unsorted) {
int[] result = new int[unsorted.length];
for (int i = 0; i < unsorted.length; i++) {
result[i] = countSmaller(unsorted[i], 0, i + 1, unsorted);
}
return result;
}
private static int countSmaller(int currentNumber, int count, int index, int[] arr) {
if (index >= arr.length) {
return count;
}
return arr[index] < currentNumber
? countSmaller(currentNumber, count + 1, index + 1, arr)
: countSmaller(currentNumber, count, index + 1, arr);
}
}
I agree with comments questioning whether recursion is your best solution here, but if it's a requirement you can avoid stack overflow by chopping subproblems in half rather than whittling them down one-by-one. The logic is that the count in the remaining data will be the sum of the count in the first half plus the count in the second half of the remaining data. This reduces the stack growth from O(n) to O(log n).
I originally did a Python implementation due to not having a Java compiler installed (plus my Java skills being rusty), but found an online java compiler at tutorialspoint.com. Here's an implementation of the divide and conquer logic described in the previous paragraph:
public class Smaller {
public static int[] smaller(int[] unsorted) {
int[] result = new int[unsorted.length];
for (int i = 0; i < unsorted.length; i++) {
result[i] = countSmaller(unsorted[i], i+1, unsorted.length-1, unsorted);
}
return result;
}
private static int countSmaller(int threshold, int start, int end, int[] unsorted) {
if (start < end) {
int mid = start + (end - start) / 2;
int count = countSmaller(threshold, start, mid, unsorted);
count += countSmaller(threshold, mid+1, end, unsorted);
return count;
} else if ((start == end) && (unsorted[start] < threshold)) {
return 1;
}
return 0;
}
}
With O(log n) stack growth, this should be able to handle ridonculously big arrays. Since the algorithm as a whole is O(n2), run time will limit you long before recursive stack limitations will.
Original Python Implementation
Sorry for showing this in Python, but I don't have a Java compiler and I didn't want to risk non-functioning code. The following does the trick and should be easy for you to translate:
def smaller(unsorted):
result = []
for i in range(len(unsorted)):
result.append(countSmaller(unsorted[i], i+1, len(unsorted)-1, unsorted))
return result
def countSmaller(threshold, start, end, unsorted):
if start < end:
mid = start + (end - start) // 2 # double slash is integer division
count = countSmaller(threshold, start, mid, unsorted)
count += countSmaller(threshold, mid+1, end, unsorted)
return count
elif start == end and unsorted[start] < threshold:
return 1
return 0
data = [10, 9, 8, 11, 7, 6]
print(smaller(data)) # [4, 3, 2, 2, 1, 0]
print(smaller([])) # []
print(smaller([42])) # [0]

Recursion method that finds the sum between two numbers

I want to implement a recursive method that finds the sum of x consecutive integers, starting from a starting number and ending with an end number.
For example, if start = 0 and end = 3, the method returns 6 (0+1+2+3). I tried to come up with the right code but I'm new to recursion and I couldn't find the right base case.
public static int summation(int start, int end) {
if (start == end) {
return 0;
}
else {
return summation(end, end - 1);
}
}
This works but make sure that start is less than end.
public static int summation(int start, int end) {
if(start < end) return start + summation(start + 1, end);
else return end;
}
If you want to use the method to calculate sum between start and end even if start > end, then use this code:
public static int summation(int start, int end) {
if(start < end) return start + summation(start + 1, end);
else if(start > end) {
start += end;
end = start - end;
start -= end;
return summation(start, end);
}
else return end;
}
If recursion is not compulsory, you can use the formula for sum of Arithmetic Progression which you learn in HighSchool Algebra.
Formula for Sum of an AP is
S = n/2 * (2a + (n-1) * d)
In your case as you have to find the sum of consequetive terms, this formula simplifies to:
S = n/2 * (2a + n -1 )
Here, a is the starting term and n is the x consequetive integers from start to end.
public long sum(int start, int end) {
int n = end - start + 1;
return n/2 * (2L * start + n - 1);
}
Works even if start and end are negative. Just make sure start < end.
Recursion is a terrible approach here. Let's apply the method Gauss used in elementary school, the sum of the numbers 1 to n is found by taking of half of n multiplied by n + 1. Like,
private static long sumN(int n) {
return ((1L + n) * n) / 2;
}
Then you can use that to find summation like
private static long summation(int start, int end) {
return sumN(end) - sumN(start);
}
No recursion (or iteration) required.

Recursion Logical Error Java

I have this assignment for school and it wants me to use recursion. I'm new to recursion and I understand it but I just can't figure out why this method isn't working the way it's suppose to. These are the instructions that were given to me and this is my code
// This method will be completed by the student!
// The student should implement the binary search algorithm using recursion. The
// method will originally be called by the GUI logic when the Search button is clicked.
public int binarySearch(Integer[] targetArray, int targetValue, int lowIndex, int highIndex){
if (lowIndex > highIndex)
return -1;
int midIndex = lowIndex + (highIndex - lowIndex)/2;
if (targetValue == targetArray[midIndex])
return midIndex;
if(targetArray[midIndex] > targetValue)
binarySearch(targetArray, targetValue, lowIndex, midIndex - 1);
else if(targetArray[midIndex] < targetValue)
binarySearch(targetArray, targetValue, midIndex + 1, highIndex);
return -1; // replace this with your recursive binary search code
}
The program will ask the user to enter in a target value. It will then search an array using recursion to tell if the target value is in the array. The array holds the numbers {1, 3, 5, 6, 8, 9, 10, 12, 14, 15}. When I search for the number 5 a message box pops up and says "Number 5 not found!" but when I set the target value to 8 it finds it in the array
I take it the comment stems from the review?
public int binarySearch(int[] targetArray, int targetValue,
int lowIndex, int highIndex) {
if (lowIndex > highIndex)
return -1;
int midIndex = lowIndex + (highIndex - lowIndex)/2;
if (targetValue == targetArray[midIndex])
return midIndex;
if (targetArray[midIndex] > targetValue)
return binarySearch(targetArray, targetValue, lowIndex, midIndex - 1);
else //if(targetArray[midIndex] < targetValue)
return binarySearch(targetArray, targetValue, midIndex + 1, highIndex);
}
The solution is to remove the last else-if.
Also you did not return the results of the recursively found index.
(An int[] parameter instead of Integer[] would be better.)
Also normally (99% of the) programmers use {} with if.
Well I have figured out the solution. The if-else statement was suppose to return the values at the end.
public int binarySearch(Integer[] targetArray, int targetValue, int lowIndex, int highIndex){
if (lowIndex > highIndex)
return -1;
int midIndex = lowIndex + (highIndex - lowIndex)/2;
if (targetValue == targetArray[midIndex])
return midIndex;
if(targetArray[midIndex] > targetValue)
return binarySearch(targetArray, targetValue, lowIndex, midIndex - 1);
else //if(targetArray[midIndex] < targetValue)
return binarySearch(targetArray, targetValue, midIndex + 1, highIndex);
//return -1; // replace this with your recursive binary search code
}

binary search, count duplicates

I have sorted array
{1,2,3,5,5,5,7,8,8}
I would like to count how many times the number that i am sending is found in the array in longn only.
for example:
public static int count(int[] array,5)
will reply 3
public static int count(int[] array,8)
will reply 2
so my plan is:
1) to do a binary search to find the number
2) binary search the top border index and the bottom border index.
3) print (top index - bottom index) will give me the time of target number in the array.
Is my code is logn ?
Please help! :)
public class binarySearch
{
public static void main(String[]args)
{
System.out.println("d");
int[]data={1,1,2,3,1,1,1};
System.out.println(count(data,1));
}
public static int count(int[] a, int x)
{
int low=0;
int high = a.length-1;
int count=0;
while(low <=high)
{
int mid=((low+high)/2);
if(x>a[mid])
low=mid+1;
if(x<a[mid])
high=mid-1;
if(x==a[mid])
{
int top=findTopIndex(a,x,mid);
int bottom=findBottomIndex(a,x,mid);
return (top-bottom);
}
}
return 111111111;
}
public static int findTopIndex(int[] a, int x, int index)
{
int low=index;
int high = a.length-1;
int mid;
if(x==a[high])
return high;
while(low <= high)
{
mid=((low+high)/2);
if(x<a[mid]&&x==a[mid-1])
return mid-1;
else if(x==a[mid])
low=mid+1;
else if(a[mid]>x && a[mid-1]!=x)
high=mid-1;
}
return 11111111;
}
public static int findBottomIndex(int[] a, int x, int index)
{
int low=0;
int high = index-1;
int mid;
if(x==a[low])
return low-1;
while(low <= high)
{
mid=((low+high)/2);
if(x>a[mid]&&x==a[mid+1])
return mid;
else if(x==a[mid])
high=mid-1;
else if(a[mid]<x && a[mid+1]!=x)
low=mid+1;
}
return 111;
}
}
What you have written is really close to the solution you need. You first do a binary search to find a single instance of the number you are searching for(let's say its found on position index) and then you do two more binary searches -one for the sequence 0, index, and one for index, size to find up to where in both sequences is the number found.
So I suggest you simply pass an index to both findTopIndex and findBottomIndex and make use of it. I can write the whole solution but it will be better for you to come to it on your own.

Using Binary Search with sorted Array with duplicates [duplicate]

This question already has answers here:
Finding multiple entries with binary search
(15 answers)
Closed 3 years ago.
I've been tasked with creating a method that will print all the indices where value x is found in a sorted array.
I understand that if we just scanned through the array from 0 to N (length of array) it would have a running time of O(n) worst case. Since the array that will be passed into the method will be sorted, I'm assuming that I can take advantage of using a Binary Search since this will be O(log n). However, this only works if the array has unique values. Since the Binary Search will finish after the first "find" of a particular value. I was thinking of doing a Binary Search for finding x in the sorted array, and then checking all values before and after this index, but then if the array contained all x values, it doesn't seem like it would be that much better.
I guess what I'm asking is, is there a better way to find all the indices for a particular value in a sorted array that is better than O(n)?
public void PrintIndicesForValue42(int[] sortedArrayOfInts)
{
// search through the sortedArrayOfInts
// print all indices where we find the number 42.
}
Ex: sortedArray = { 1, 13, 42, 42, 42, 77, 78 } would print: "42 was found at Indices: 2, 3, 4"
You will get the result in O(lg n)
public static void PrintIndicesForValue(int[] numbers, int target) {
if (numbers == null)
return;
int low = 0, high = numbers.length - 1;
// get the start index of target number
int startIndex = -1;
while (low <= high) {
int mid = (high - low) / 2 + low;
if (numbers[mid] > target) {
high = mid - 1;
} else if (numbers[mid] == target) {
startIndex = mid;
high = mid - 1;
} else
low = mid + 1;
}
// get the end index of target number
int endIndex = -1;
low = 0;
high = numbers.length - 1;
while (low <= high) {
int mid = (high - low) / 2 + low;
if (numbers[mid] > target) {
high = mid - 1;
} else if (numbers[mid] == target) {
endIndex = mid;
low = mid + 1;
} else
low = mid + 1;
}
if (startIndex != -1 && endIndex != -1){
for(int i=0; i+startIndex<=endIndex;i++){
if(i>0)
System.out.print(',');
System.out.print(i+startIndex);
}
}
}
Well, if you actually do have a sorted array, you can do a binary search until you find one of the indexes you're looking for, and from there, the rest should be easy to find since they're all next to each-other.
once you've found your first one, than you go find all the instances before it, and then all the instances after it.
Using that method you should get roughly O(lg(n)+k) where k is the number of occurrences of the value that you're searching for.
EDIT:
And, No, you will never be able to access all k values in anything less than O(k) time.
Second edit: so that I can feel as though I'm actually contributing something useful:
Instead of just searching for the first and last occurrences of X than you can do a binary search for the first occurence and a binary search for the last occurrence. which will result in O(lg(n)) total. once you've done that, you'll know that all the between indexes also contain X(assuming that it's sorted)
You can do this by searching checking if the value is equal to x , AND checking if the value to the left(or right depending on whether you're looking for the first occurrence or the last occurrence) is equal to x.
public void PrintIndicesForValue42(int[] sortedArrayOfInts) {
int index_occurrence_of_42 = left = right = binarySearch(sortedArrayOfInts, 42);
while (left - 1 >= 0) {
if (sortedArrayOfInts[left-1] == 42)
left--;
}
while (right + 1 < sortedArrayOfInts.length) {
if (sortedArrayOfInts[right+1] == 42)
right++;
}
System.out.println("Indices are from: " + left + " to " + right);
}
This would run in O(log(n) + #occurrences)
Read and understand the code. It's simple enough.
Below is the java code which returns the range for which the search-key is spread in the given sorted array:
public static int doBinarySearchRec(int[] array, int start, int end, int n) {
if (start > end) {
return -1;
}
int mid = start + (end - start) / 2;
if (n == array[mid]) {
return mid;
} else if (n < array[mid]) {
return doBinarySearchRec(array, start, mid - 1, n);
} else {
return doBinarySearchRec(array, mid + 1, end, n);
}
}
/**
* Given a sorted array with duplicates and a number, find the range in the
* form of (startIndex, endIndex) of that number. For example,
*
* find_range({0 2 3 3 3 10 10}, 3) should return (2,4). find_range({0 2 3 3
* 3 10 10}, 6) should return (-1,-1). The array and the number of
* duplicates can be large.
*
*/
public static int[] binarySearchArrayWithDup(int[] array, int n) {
if (null == array) {
return null;
}
int firstMatch = doBinarySearchRec(array, 0, array.length - 1, n);
int[] resultArray = { -1, -1 };
if (firstMatch == -1) {
return resultArray;
}
int leftMost = firstMatch;
int rightMost = firstMatch;
for (int result = doBinarySearchRec(array, 0, leftMost - 1, n); result != -1;) {
leftMost = result;
result = doBinarySearchRec(array, 0, leftMost - 1, n);
}
for (int result = doBinarySearchRec(array, rightMost + 1, array.length - 1, n); result != -1;) {
rightMost = result;
result = doBinarySearchRec(array, rightMost + 1, array.length - 1, n);
}
resultArray[0] = leftMost;
resultArray[1] = rightMost;
return resultArray;
}
Another result for log(n) binary search for leftmost target and rightmost target. This is in C++, but I think it is quite readable.
The idea is that we always end up when left = right + 1. So, to find leftmost target, if we can move right to rightmost number which is less than target, left will be at the leftmost target.
For leftmost target:
int binary_search(vector<int>& nums, int target){
int n = nums.size();
int left = 0, right = n - 1;
// carry right to the greatest number which is less than target.
while(left <= right){
int mid = (left + right) / 2;
if(nums[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
// when we are here, right is at the index of greatest number
// which is less than target and since left is at the next,
// it is at the first target's index
return left;
}
For the rightmost target, the idea is very similar:
int binary_search(vector<int>& nums, int target){
while(left <= right){
int mid = (left + right) / 2;
// carry left to the smallest number which is greater than target.
if(nums[mid] <= target)
left = mid + 1;
else
right = mid - 1;
}
// when we are here, left is at the index of smallest number
// which is greater than target and since right is at the next,
// it is at the first target's index
return right;
}
I came up with the solution using binary search, only thing is to do the binary search on both the sides if the match is found.
public static void main(String[] args) {
int a[] ={1,2,2,5,5,6,8,9,10};
System.out.println(2+" IS AVAILABLE AT = "+findDuplicateOfN(a, 0, a.length-1, 2));
System.out.println(5+" IS AVAILABLE AT = "+findDuplicateOfN(a, 0, a.length-1, 5));
int a1[] ={2,2,2,2,2,2,2,2,2};
System.out.println(2+" IS AVAILABLE AT = "+findDuplicateOfN(a1, 0, a1.length-1, 2));
int a2[] ={1,2,3,4,5,6,7,8,9};
System.out.println(10+" IS AVAILABLE AT = "+findDuplicateOfN(a2, 0, a2.length-1, 10));
}
public static String findDuplicateOfN(int[] a, int l, int h, int x){
if(l>h){
return "";
}
int m = (h-l)/2+l;
if(a[m] == x){
String matchedIndexs = ""+m;
matchedIndexs = matchedIndexs+findDuplicateOfN(a, l, m-1, x);
matchedIndexs = matchedIndexs+findDuplicateOfN(a, m+1, h, x);
return matchedIndexs;
}else if(a[m]>x){
return findDuplicateOfN(a, l, m-1, x);
}else{
return findDuplicateOfN(a, m+1, h, x);
}
}
2 IS AVAILABLE AT = 12
5 IS AVAILABLE AT = 43
2 IS AVAILABLE AT = 410236578
10 IS AVAILABLE AT =
I think this is still providing the results in O(logn) complexity.
A Hashmap might work, if you're not required to use a binary search.
Create a HashMap where the Key is the value itself, and then value is an array of indices where that value is in the array. Loop through your array, updating each array in the HashMap for each value.
Lookup time for the indices for each value will be ~ O(1), and creating the map itself will be ~ O(n).
Find_Key(int arr[], int size, int key){
int begin = 0;
int end = size - 1;
int mid = end / 2;
int res = INT_MIN;
while (begin != mid)
{
if (arr[mid] < key)
begin = mid;
else
{
end = mid;
if(arr[mid] == key)
res = mid;
}
mid = (end + begin )/2;
}
return res;
}
Assuming the array of ints is in ascending sorted order; Returns the index of the first index of key occurrence or INT_MIN. Runs in O(lg n).
It is using Modified Binary Search. It will be O(LogN). Space complexity will be O(1).
We are calling BinarySearchModified two times. One for finding start index of element and another for finding end index of element.
private static int BinarySearchModified(int[] input, double toSearch)
{
int start = 0;
int end = input.Length - 1;
while (start <= end)
{
int mid = start + (end - start)/2;
if (toSearch < input[mid]) end = mid - 1;
else start = mid + 1;
}
return start;
}
public static Result GetRange(int[] input, int toSearch)
{
if (input == null) return new Result(-1, -1);
int low = BinarySearchModified(input, toSearch - 0.5);
if ((low >= input.Length) || (input[low] != toSearch)) return new Result(-1, -1);
int high = BinarySearchModified(input, toSearch + 0.5);
return new Result(low, high - 1);
}
public struct Result
{
public int LowIndex;
public int HighIndex;
public Result(int low, int high)
{
LowIndex = low;
HighIndex = high;
}
}
public void printCopies(int[] array)
{
HashMap<Integer, Integer> memberMap = new HashMap<Integer, Integer>();
for(int i = 0; i < array.size; i++)
if(!memberMap.contains(array[i]))
memberMap.put(array[i], 1);
else
{
int temp = memberMap.get(array[i]); //get the number of occurances
memberMap.put(array[i], ++temp); //increment his occurance
}
//check keys which occured more than once
//dump them in a ArrayList
//return this ArrayList
}
Alternatevely, instead of counting the number of occurances, you can put their indices in a arraylist and put that in the map instead of the count.
HashMap<Integer, ArrayList<Integer>>
//the integer is the value, the arraylist a list of their indices
public void printCopies(int[] array)
{
HashMap<Integer, ArrayList<Integer>> memberMap = new HashMap<Integer, ArrayList<Integer>>();
for(int i = 0; i < array.size; i++)
if(!memberMap.contains(array[i]))
{
ArrayList temp = new ArrayList();
temp.add(i);
memberMap.put(array[i], temp);
}
else
{
ArrayList temp = memberMap.get(array[i]); //get the lsit of indices
temp.add(i);
memberMap.put(array[i], temp); //update the index list
}
//check keys which return lists with length > 1
//handle the result any way you want
}
heh, i guess this will have to be posted.
int predefinedDuplicate = //value here;
int index = Arrays.binarySearch(array, predefinedDuplicate);
int leftIndex, rightIndex;
//search left
for(leftIndex = index; array[leftIndex] == array[index]; leftIndex--); //let it run thru it
//leftIndex is now the first different element to the left of this duplicate number string
for(rightIndex = index; array[rightIndex] == array[index]; rightIndex++); //let it run thru it
//right index contains the first different element to the right of the string
//you can arraycopy this [leftIndex+1, rightIndex-1] string or just print it
for(int i = leftIndex+1; i<rightIndex; i++)
System.out.println(array[i] + "\t");

Categories

Resources