BinarySearch using Recursion in Java - java

I am learning recursion so trying to practise it by implementing BinarySearch algorithm.
public class BinarySearch {
public int search(int[] items, int searchTerm, int startIndex, int endIndex) {
int midIndex = (startIndex + endIndex)/2 ;
System.out.println("Midpoint: "+midIndex);
if(searchTerm == items[midIndex]) {
return midIndex;
} else if(searchTerm > items[midIndex]) {
search(items, searchTerm, midIndex, endIndex);
} else if(searchTerm < items[midIndex]) {
search(items, searchTerm, startIndex, midIndex);
} else {
return -1;
}
return -1;
}
public static void main(String[] args) {
BinarySearch bs = new BinarySearch();
int[] items = { 1, 7, 9, 11, 22, 55, 67 };
int index = bs.search(items, 7, 0, items.length-1);
System.out.println(index);
}
}
search is the method that is called recursively with array of items, the value that I want to search and start & end index of the array where item could potentially be there.
First, I get the midpoint and if the item at midpoint matches the searchTerm then I return it. If searchTerm is greater than value at midpoint then I call the search method again with midpoint as startIndex and last item in the array as endIndex.
Similarly, if searchTerm is less than value at midpoint, then pass startIndex and midpoint as start and end value to search in.
Lets say, I want to search for 7. In the first iteration, 3 would be the midpoint so value at index 3 would be 11. Since, 7 < 11, search will be called again with startIndex as 0 and endIndex as 3. Now, midpoint would be 7, so the index of 7 (which is 1) should be returned. However, the value returned is -1 which is not correct.
When I tried troubleshooting it using Eclipse debugger, I found that even after the match is found at
if(searchTerm == items[midIndex]) {
return midIndex;
}
instead of exiting the method, the below code gets executed
else if(searchTerm < items[midIndex]) {
search(items, searchTerm, startIndex, midIndex);
}
And then it returns -1. I feel it could be something to do with recursion as the previous invocation of search() would still be in the stack due to recursive nature of the code and it might be popping out of the stack. However, I can't really understand it. What would be the correct way to implement BinarySearch using recursion?

public int search(int[] items, int searchTerm, int startIndex, int endIndex)
{
int midIndex = (startIndex + endIndex) / 2;
if (startIndex > endIndex)
return -1;
else if (searchTerm == items[midIndex])
return midIndex;
else if (searchTerm < items[midIndex])
return search(items, searchTerm, startIndex, midIndex - 1);
else // (searchTerm > items[midIndex])
return search(items, searchTerm, midIndex + 1, endIndex);
}
In this version of the binary search I used midIndex ± 1: I did this because in your if statement you already check if items[midIndex] is equal or not to searchTerm, that's why considering items[midIndex] in the next calls is unnecessary.

You are not returning your recursive calls.
public int search(int[] items, int searchTerm, int startIndex, int endIndex) {
int midIndex = (startIndex + endIndex)/2 ;
System.out.println("Midpoint: "+midIndex);
if(searchTerm == items[midIndex]) {
return midIndex;
} else if(searchTerm > items[midIndex]) {
return search(items, searchTerm, midIndex, endIndex);
} else if(searchTerm < items[midIndex]) {
return search(items, searchTerm, startIndex, midIndex);
} else {
return -1;
}
return -1;
}
UPDATE
Recursion works with a Method Stack as you may know. So when you don't return the value back when you find the element, Your final recursion call will always return -1 as it is the last line in the method.
That is why you got -1.

Related

Recursion sum numbers *with rule* - Recursion only

So the exercise is:
Using recursion only (no loops)
Find if there is sub ground of numbers that are equal to the given number in an array and follow the rule.
Let's say I have this array, I give the function a number for sum and it must adhere to this rule:
you cannot repeat the same number, and you can't sum 3 numbers in a row (can't do i+1 and i+2)
int[] a = {5,4,2,1,3};
So in this case:
num 8 = true (4+3+1) ( 5+3)
num 11 = false (4+5+2 are 3 but are three in a row) (5+2+1+3 also three in a row)
My attempt is:
public static boolean sumRule(int[] a , int num){
if (num == 0){
return true;
} else {
return sumRule(a,num,0,a.length);
}
}
private static boolean sumRule(int[] a, int num, int low,int high){
if(low >= high || low < 0){
return false;
}
if (a[low] == -1){
return false;
}
if(a[low] == num || num-a[low] == 0 ){
return true;
}
int temp = a[low];
a[low] = -1;
return sumRule(a,num,low,high) || sumRule(a,num-temp,low+3,high) || sumRule(a,num-temp,low+1,high) ;
}
But when I send 11 to this, it still returns true, anyone has an idea what am i missing here?
Thanks,
I have the full code answer below, and here's the explanation:
Essentially you need to break this problem down to a recurrence. What that means is, you look at the choice at each step (i.e. whether to use a number or not in the sum) and recursively calculate both options.
The base case:
If num == 0 then we know it's true. But if num != 0 and the array has length 0, then we know it's false
Recursive case:
We check if the first number in the array is less than or equal to num. If not, then we can't use it. So we do a recursive call with all the same parameters except the array is now the original array minus the first number
If we CAN use the number (i.e. a[0] <= num) then the true answer might use this or it may not use it. We make a recursive call for each case, and return true if either of the recursive calls return true.
The consecutive number rule:
This is easy to enforce. We add a parameter called 'left' which tells us the number of elements we can consecutively take from the beginning of the array. To start with, left is 2 because at most we can take 2 consecutive numbers. Then in the cases where we DO use the first number in the array in our sum, we decrement left. If we don't use the first number in the array, we reset left to 2. In the cases where left becomes 0, we have no choice but to skip the current number at the top of the array.
class Main {
public static void main(String[] args) {
int[] a = new int[] {5,4,2,1,3};
System.out.println(sumRule(a, 8));
System.out.println(sumRule(a, 11));
}
public static boolean sumRule(int[] a , int num){
if (num == 0){
return true;
} else {
return sumRule(a,num,2);
}
}
private static boolean sumRule(int[] a, int num, int left){
if (num == 0) {
return true;
}
if (a.length == 0) {
return false;
}
int[] a_new = new int[a.length-1];
for (int i = 1; i < a.length; i++) a_new[i-1] = a[i];
if (left == 0) {
return sumRule(a_new, num, 2);
}
boolean using_a0 = false;
if (a[0] <= num) {
using_a0 = sumRule(a_new, num-a[0], left-1);
}
boolean not_using_a0 = sumRule(a_new, num, 2);
return (not_using_a0 || using_a0);
}
}
Edit - A variation on the code above without copying the array:
class Main {
public static void main(String[] args) {
int[] a = new int[] {5,4,2,1,3};
System.out.println(sumRule(a, 8));
System.out.println(sumRule(a, 11));
}
public static boolean sumRule(int[] a , int num){
if (num == 0){
return true;
} else {
return sumRuleNoLoop(a,num,2,0);
}
}
private static boolean sumRuleNoLoop(int[] a, int num, int left, int startIdx){
if (num == 0) {
return true;
}
if (startIdx >= a.length) {
return false;
}
if (left == 0) {
return sumRuleNoLoop(a, num, 2, startIdx+1);
}
boolean using_a0 = false;
if (a[startIdx] <= num) {
using_a0 = sumRuleNoLoop(a, num-a[startIdx], left-1, startIdx+1);
}
boolean not_using_a0 = sumRuleNoLoop(a, num, 2, startIdx+1);
return (not_using_a0 || using_a0);
}
}
First thing you can add is a check to see not 3 numbers in a row being added. Also replacing a number in the array with -1 would have unintended side effects within recursive calls. Below is something I have. You can ignore the index param I have used to see the values used.
Explanation:
The recursive sumRule method divides the problem into two parts:
First part takes the value of current index and adds with the sum of values starting from next index.
Second part assumes, current value can’t be taken for the sum. It only checks if there is a sum within the subset starting from next value of the array.
In the method, lastIndex is keeping track of the index of last value picked up for the sum. So, in the first call the value is 0, 1 in second and so on.
(start - lastIndex <= 1 ? consecutive + 1 : 1) is to check whether value of consecutive should be increased or not. consecutive = 1 means, current value is added to the sum.
public static boolean sumRule(int[] a, int num) {
if (num == 0) {
return true;
} else {
return sumRule(a, num, 0, 0, 0, 0, "");
}
}
public static boolean sumRule(final int[] a, int num, int sum, int start, int consecutive, int lastIndex,
String index) {
if (consecutive == 3) {
return false;
}
if (sum == num) {
System.out.println(index);
return true;
}
if (start >= a.length) {
return false;
}
return sumRule(a, num, sum + a[start], start + 1, (start - lastIndex <= 1 ? consecutive + 1 : 1), start,
index + ", " + start) || sumRule(a, num, sum, start + 1, consecutive, lastIndex, index);
}
Here is my implementation. It contains comments explaining what the different parts do.
public class RecurSum {
/**
* Determines whether 'sum' equals 'target'.
*
* #param arr - its elements are summed
* #param sum - sum of some elements in 'arr'
* #param target - required value of 'sum'
* #param index - index in 'arr'
* #param consecutive - number of consecutive indexes summed to ensure don't exceed 3
* #param start - starting element in 'arr' which is used for back-tracking
*
* #return "true" if 'sum' equals 'target'
*/
private static boolean sumRule(int[] arr, int sum, int target, int index, int consecutive, int start) {
if (sum == target) {
return true;
}
else {
if (index >= arr.length) {
// if we have reached last element in 'arr' then back-track and start again
if (start < arr.length) {
return sumRule(arr, 0, target, start + 1, 0, start + 1);
}
// we have reached last element in 'arr' and cannot back-track
return false;
}
else {
consecutive++;
if (consecutive == 3) {
// skip 3rd consecutive element (because of the rule)
consecutive = 0;
return sumRule(arr, sum, target, index + 2, consecutive, start);
}
else {
if (sum + arr[index] > target) {
// recursive call but don't add current element of 'arr'
return sumRule(arr, sum, target, index + 1, 0, start);
}
// recursive call: add current element of 'arr' to 'sum' and proceed to next element
return sumRule(arr, sum + arr[index], target, index + 1, consecutive, start);
}
}
}
}
public static void main(String[] args) {
int[] arr = new int[]{5, 4, 2, 1, 3};
// initial call to recursive method with target = 11 (eleven)
System.out.println(sumRule(arr, 0, 11, 0, 0, 0));
// initial call to recursive method with target = 8
System.out.println(sumRule(arr, 0, 8, 0, 0, 0));
}
}

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
}

How to find max int in an array, while ignoring all numbers greater than first value?

Having trouble with this problem on an assignment I'm working on. I believe the problem is that the method will never return a negative value, but I'm not sure why that is or how to fix it. (Also, this has to be done using recursion, even though that is probably the worst way to accomplish it.
private static int findMaxOfLessThanFirst(int[] numList, int startIndex, int endIndex, int firstNum) {
if(startIndex>-1&&startIndex<100){
if(startIndex==endIndex){
if(numList[startIndex]<=firstNum){
return numList[startIndex];
} else {
return 222; //number used for testing
}
}
if(numList[startIndex]>=
findMaxOfLessThanFirst(numList,startIndex+1,endIndex,firstNum) &&
numList[startIndex]<=firstNum){
return numList[startIndex];
} else {
return findMaxOfLessThanFirst(numList,startIndex+1,endIndex,firstNum);
}
} return 333; //number used for testing
}
The goal is to return the integer in the array that is largest while ignoring all of the numbers that are larger than numList[0].
private static int findMaxOfLessThanFirst(int[] numList, int currentIndex, int len, int currentMax) {
// when you reach the end of the array, return the currentMax
if (currentIndex == len - 1) {
return currentMax;
}
int firstElement = numList[0];
// if the current element we are looking at in this stage of the recursion is greater
// than the first element, ignore it and recurse on the next element
if (numList[currentIndex] >= firstElement) {
return findMaxOfLessThanFirst(numList, currentIndex + 1, len, currentMax);
}
int newMax;
// if none of the previous conditions were true, and this current value is greater than
// the currentMax, update the currentMax
if (numList[currentIndex] > currentMax) {
newMax = numList[currentIndex];
}
return findMaxOfLessThanFirst(numList, currentIndex + 1, len, newMax);
}
When you call this function, set your currentMax to something like the minimum integer value. This way you can check the return value at the end to see if you did, in fact, have an element less than the first element.

Error in program when parsing negative integers into an array

I am having a strange issue with one of my assignments. I am attempting to take integers from user input and store them in an array. After that, four recursive methods will be run on them to find different characteristics of those numbers. However, whenever I attempt to run the program with a negative integer in any of the indexes the program stops responding.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Assignment9 {
public static void main(String[] args) throws IOException {
int index = 0;
int[] numbers;
numbers = new int[100];
InputStreamReader inRead = new InputStreamReader(System.in);
BufferedReader buffRead = new BufferedReader(inRead);
String line = buffRead.readLine();
try {
while (!line.equals("0") && index < 100) {
numbers[index] = Integer.parseInt(line);
index++;
line = buffRead.readLine();
}
} catch (IOException exception) {
System.out.println("Array index out of bound");
}
` int min = findMin(numbers, 0, numbers.length - 1);
int sumAtEven = computeSumAtEvenIndexes(numbers, 0, numbers.length - 1);
int divByThree = countDivisibleBy3(numbers, 0, numbers.length - 1);
System.out.println("The minimum number is " + min);
System.out.println("The sum of numbers at even indexes is " + sumAtEven);
System.out.println("The count of numbers that are divisible by 3 is " + divByThree);
System.out.println("The maximum number among numbers that are less than the first number is " + maxLessThanFirst);
}
public static int findMin(int[] numbers, int startIndex, int endIndex) {
if (startIndex == endIndex) {
return numbers[startIndex];
} else if (findMin(numbers, startIndex, endIndex - 1) < numbers[endIndex]) {
return findMin(numbers, startIndex, endIndex - 1);
} else {
return numbers[endIndex];
}
}
public static int computeSumAtEvenIndexes(int[] numbers, int startIndex, int endIndex) {
if (startIndex == endIndex) {
if (startIndex % 2 == 0) {
return numbers[startIndex];
} else return 0;
} else {
if (endIndex % 2 == 0) {
return computeSumAtEvenIndexes(numbers, startIndex, endIndex - 1) + numbers[endIndex];
} else {
return computeSumAtEvenIndexes(numbers, startIndex, endIndex - 1);
}
}
}
public static int countDivisibleBy3(int[] numbers, int startIndex, int endIndex) {
if (startIndex == endIndex) {
if (numbers[startIndex] % 3 == 0) {
return 1;
} else {
return 0;
}
} else {
if (numbers[endIndex] == 0) {
return countDivisibleBy3(numbers, startIndex, endIndex - 1);
}
if (numbers[endIndex] % 3 == 0) {
return countDivisibleBy3(numbers, startIndex, endIndex - 1) + 1;
} else {
return countDivisibleBy3(numbers, startIndex, endIndex - 1);
}
}
}
}
This is the only relevant section of code that is necessary to understand the problem, I believe. If additional code is needed just ask. Thank you!
Replace your findMin method. Pass the array and zero for the index;
public static int findMin(int[] numbers, int index) {
if (index == numbers.length - 1)
{
return numbers[index];
}
else
{
return Math.min(numbers[index], findMin(numbers, index + 1));
}
}
Your findMin method is doubly recursive:
public static int findMin(int[] numbers, int startIndex, int endIndex) {
if (startIndex == endIndex) {
return numbers[startIndex];
// in the next line, we recurse looking for the minimum
} else if (findMin(numbers, startIndex, endIndex - 1) < numbers[endIndex]) {
// we've found the minimum, but now we must recurse again to get it!
return findMin(numbers, startIndex, endIndex - 1);
} else {
return numbers[endIndex];
}
}
This transforms what should be a linear algorithm into an exponential one. If you log each time findMin is entered, you will find it increases rapidly with the size of the array. Experimentation shows that it is called 2^(x-1) + 2^(x-2) - 1 times, where x is the length of the array.
So if you had an array of size 10, it would be called 767 times. For an array of size 20, it would be called 786,431 times. Size 30, 25,165,823 times. Size 100 yields:
950,737,950,171,172,051,122,527,404,031 calls (950 octillion, 9.5 x 10^29).
Your program will not crash with a StackOverflowError because at no time does the stack depth exceed roughly the length of the array (plus one for the main), but it will take longer than the lifespan of the universe to run.
Changing findMin to this:
public static int findMin(int[] numbers, int startIndex, int endIndex) {
if (startIndex == endIndex) {
return numbers[startIndex];
}
// there's no need for an else since the if ended with a return
int r = findMin(numbers, startIndex, endIndex - 1); // only recurse once
if (r < numbers[endIndex]) {
// we've found the minimum, return it without recursing again
return r;
}
// again, no need for else, here
return numbers[endIndex];
}
Now the algorithm only takes 20 calls for an array of size 20, 50 for size 50, 100 for size 100. The fact that you had already called findMin recursively to get the value to compare but were then calling it again to get the same value to return should have been a red flag. In many cases, such unnecessary repetition would merely be a minor annoyance; in this case, it was catastrophic.
Oh, I forgot to mention. The reason entering a negative number triggered the problem: Presumably, you don't always type 100 numbers in. I never did when I tried your program. It's too boring, and you can just end the list by entering a zero. When you allocate the array:
int[] numbers = new int[100];
The array is filled with zeros. So, if you enter ten numbers, the rest of the array will be zeros, and when we get to this line in the first call to findMin:
if (findMin(numbers, startIndex, endIndex - 1) < numbers[endIndex])
The number at the end will be zero, which is also the min. So we only recurse once, not twice. That will happen for most of the recursive calls, too; it will happen for all the ones at the long tail of zeros at the end of the array. It will only be when we get down into the entries where the user has actually entered a value where we will see the doubly-recursive behavior. If the user only enters about 10 numbers, we're safe.
But if the user enters even a single negative number, then that becomes the minimum and the zeros don't protect us. We hit the double recursion on every call, except for the one where numbers[endIndex] holds that negative number. Come to think of it, the reason I got 2^(x-1) + 2^(x-2) - 1 instead of just 2^x - 1 is because I always put the negative number in the second position in the array. So, depending on where the minimum value is, you could get performance better, or even worse, than what I describe above.

Find the sum of all elements in array recursively in java language

Here is my code:
public int sum(int[] array, int index)
{
//int index is the number of elements in the array.
//Here is my base case:
if (index == 0)
return 0;
//Now it's time for the recursion
else
return array[index] + sum(array, index + 1);
}
I keep on getting an out of bounds error, but I don't what I am doing wrong.
Your base condition is faulty. It should be:
if (index == array.length)
Note, you need to pass index = 0 on first call. If you are passing index = array.length - 1, then keep the base case as it is, and change the recursive method invocation to pass index - 1, instead of index + 1.
However, do you really need recursion? I would seriously give it hundreds of thoughts before reaching out for recursion instead of loop for this task.
Try,
public static void main(String[] args){
int arr[] = {3, 4, 6, 7};
System.out.println(sum(arr, arr.length-1));
}
public static int sum(int[] array, int index) {
if (index == 0) {
return array[0];
} else {
return array[index] + sum(array, index - 1);
}
}
instead of going from 0 to highest index , go from highest index to 0, i did (index -1) because you said index is total elements, so if array has 10 elements, last element has index 9
public int sum(int[] array, int index)
{
//int index is the number of elements in the array.
//Here is my base case:
if (index == 0)
return 0;
//Now it's time for the recursion
else
return array[index-1] + sum(array, (index - 1);
}
# Masud - you're code has a logical error though (i'm beginner Java so sorry if i'm incorrect).
return array[index] + sum(array, index - 1);
array[index]
will receive a out-of-bounds error as index starts at 0 - hence meaning there won't be an index there.
'index - 1' would work.
Also, this would change your base case to return '0' as returning array[0] will have array[0] added twice and an incorrect sum.
This is my code:
public static int sumArrayRecursion(int array[], int n){
if (n == 0){
return 0;
}
else {
return array[n-1] + sumArrayRecursion(array, n-1);
}
}
If you are using Java 1.8 you can do the following
public int sum(int[] array)
{
return (int)array.stream().sum();
}
or even
public int sum(int[] array)
{
return (int)array.sum();
}

Categories

Resources