Recursion confusion - java

I am writing a function according to the following guidelines:
Given an array of ints, is it possible to choose a group of some of the ints, beginning at the start index, such that the group sums to the given target? However, with the additional constraint that all 6's must be chosen. (No loops needed.)
groupSum6(0, {5, 6, 2}, 8) → true
groupSum6(0, {5, 6, 2}, 9) → false
groupSum6(0, {5, 6, 2}, 7) → false
This is the solution:
public boolean groupSum6(int start, int[] nums, int target) {
if (start == nums.length) {
if (target == 0) {
return true;
}
return false;
}
if (nums[start] == 6) {
return groupSum6(start + 1, nums, target - nums[start]);
}
if (groupSum6(start + 1, nums, target - nums[start])) {
return true;
}
return groupSum6(start + 1, nums, target);
}
I am a little confused about why this works. I understand how it adds 6s, but where are the other numbers tested to see if they will add up to the target after the 6s have been added? Where are other non-6 numbers added?

The reason this works is that the recursive call always checks to see if the number at the current index is a 6 and subtracts that from the target. So the order of evaluation is:
1)
if (start == nums.length) {
if (target == 0) {
return true;
}
return false;
}
Check to see if the passed index is the length of the array (so this would mean the index is BEYOND the bounds of the array - the last index of the array would be nums[nums.length - 1] ). If yes, AND if the target is 0, return true. Otherwise return false, because there are no more values to check
2)
if (nums[start] == 6) {
return groupSum6(start + 1, nums, target - nums[start]);
}
Check if the number at the index is 6. If it is, subtract 6 from the target, add one to the index, and check the remaining values.
3) Otherwise...
if (groupSum6(start + 1, nums, target - nums[start])) {
return true;
}
Evaluate if the rest of the array works to add up to the target. If it does return true. The key is to notice that this step will always happen AFTER we check if the value is a 6. Essentially here you are looking for a solution that COUNTS the number in the start index. If a solution is found we can end. If no solution is found using the number at num[start], then we go on to:
4)
return groupSum6(start + 1, nums, target);
Checking for a solution that exists when we DON'T use the number at num[start].
The key is that each time the recursive call is made the check for a 6 is done first ( assuming you aren't beyond the bounds of the array).

The idea is to test all the possible subarray options, something like "what happen if i get next element?" and... "what happen if i don't?".
So, for any element in the array your are making two recursive calls (two options, take it or not) and... if one of them is OK the solution is reached (so return true).
In the next recursive call you pick the number, so you call with the next index element (start+1) and substract the picked number from the target (imagine your target is 8 and the current element is 2... the recursive call is to check the next numbers with target 8-2= 6).
if (groupSum6(start + 1, nums, target - nums[start])) {
return true;
}
The next recursive call is the same but you don't pick the current number so... target will be the same (in the previous example you will check the next number with target 8);
return groupSum6(start + 1, nums, target);
And... here it comes the 6 restriction... if you find a 6 then you have no more options than to pick the number so... you put that condition first (and in this scenario there is only one call because you have no options):
if (nums[start] == 6) {
return groupSum6(start + 1, nums, target - nums[start]);
}

Just do this check if the number is 6 and then decide to skip it
public boolean groupSum6(int start, int[] nums, int target) {
//exit condition and if target becomes 0
if(start >= nums.length) return (target ==0);
// subtracting the number at start from target and make a recursive //call to groupSum6
if(groupSum6(start+1 , nums , target - nums[start])) return true;
//check if the number at start is 6 or not then decide to skip it
if(nums[start] != 6 && groupSum6(start+1 , nums , target)) return true;
//if target does not becomes 0
return false;
}

Related

How do I scan elements of a 2d array under certain conditions, when using recursion in Java?

I have a two-dimensional array filled with non-negative numbers.
one cell in particular is equal to -1 (for example, the bottom right corner in the array below is -1).
I have to return the minimum number of steps (starting from arr[0][0] only) to get to the -1.
The thing is:
You can think of every number in the array as a height of a rooftop.
The difference between cell values when moving in any direction is either "climbing" or "going down":
For example, moving in any direction from a cell with the value 5, to a cell with the value 3 is considered "going down", and the difference is 2.
For example, moving in any direction from a cell with the value 1, to a cell with the value 2, is considered "climbing", and the difference is -1.
If the difference between cell values while "climbing" is smaller than -1,
and the difference between cell values while " going down" is bigger than 2,
There are no legal options to move inside the array, and the recursive method will return -1.
The only exception is: if the value of the "neighbor" cell from any direction is the -1 we are searching for, we can immediately go there without having to consider the difference between the current cell and the "neighbor" cell.
If there is no valid path that starts in arr[0][0] and can find "-1" in the array, the recursive method should return -1. If there is a valid path the recursive method returns the minimum number of steps.
System.out.println("[2, 0, 1, 2, 3]");
System.out.println("[2, 3, 5, 5, 4]");
System.out.println("[8, 5, -1, 8, 7]");
System.out.println("[3, 4, 7, 2, 4]");
System.out.println("[2, 4, 3, 1, -1]");
I didn't manage to solve it without running into "out of bounds" errors, and logic errors.
For example, what shall I do when the difference between any direction while "climbing" or "going down" is the same?
So that's what I have tried:
public static int prince(int[][]drm, int i, int j){
return prince(drm, 0,0, 1);
}
private static int prince(int[][]drm, int i, int j, int steps)
{
if(i> drm.length-1 || j>drm[drm.length-1].length) // out of bounds
{
return -1;
}
if(drm[i][j] == -1) // is the current cell is "-1"
{
return steps;
}
if(i< drm.length-1)
{
if(drm[i+1][j] == -1) // if the neighbor below the cell is the "-1"
{
return steps + 1;
}
}
if(j<drm[drm.length-1].length) // if the right neighbor of the cell is the "-1"
{
if(drm[i][j+1] == -1)
{
return steps + 1;
}
}
if(i>0) { // if the neighbor above the cell is the "-1"
if (drm[i - 1][j] == 1) {
return steps + 1;
}
}
if(j>0) // if the left neighbor of the cell is the "-1"
{
if (drm[i][j-1] == 1) {
return steps + 1;
}
}
so checking the neighbors to see if they're equal to -1 is ok. but I'm not sure how to construct the next part, that actually contains the recursive call.
I wanted to scan the array horizontally in each row, to the rightmost point, and when it gets there - to make j=0 again, go down a row (if possible) and make the number of steps as same as it would be if we would down initially instead of right.
Something amount those lines:
if(j == drm[drm.length-1].length) // if we got to the end of the row (rightmost column)
{
if(i < drm.length-1){ // and we can go down without going out of bounds
int downDiff = drm[i][j] - drm[i+1][j];
if(!(downDiff > 2) && !(downDiff < -1)){ // if the difference when going down is valid
i++;
j = 0;
steps -= drm.length - 2;
} else{
return -1;
}
}
}
if(j<drm[drm.length-1].length) { // if we didn't get to the end of the row (not in the rightmost column)
int rightDiff = drm[i][j] - drm[i][j+1];
if(rightDiff > 2 || rightDiff < -1) // if right difference is not valid
{
return -1;
}
return prince(drm, i, j+1, steps+1);
}
return -1;
}
}
so if you have any ideas, or smarter algorithms/approaches to this question I will be more than glad for any comments.

Alogrithm recursion codingbat

Recursion-2 > groupSum6
http://codingbat.com/prob/p199368
I modified the solution from the first one example where the condition of using 6 is not present.
Below working solution:
public boolean groupSum6(int start, int[] nums, int target) {
// Base case: if there are no numbers left, then there is a
// solution only if target is 0.
if (start >= nums.length) return (target == 0);
// Key idea: nums[start] is chosen or it is not.
// Deal with nums[start], letting recursion
// deal with all the rest of the array.
// Recursive call trying the case that nums[start] is chosen --
// subtract it from target in the call.
if (nums[start] == 6)
return groupSum6(start + 1, nums, target - nums[start]);
if (groupSum6(start + 1, nums, target - nums[start]))
return true;
// Recursive call trying the case that nums[start] is not chosen.
if (groupSum6(start + 1, nums, target)) return true;
// If neither of the above worked, it's not possible.
return false;
}
HOWEVER it is not working when I'd replace
if (nums[start] == 6)
return groupSum6(start + 1, nums, target - nums[start]);
with :
if (nums[start] == 6)
groupSum6(start + 1, nums, target - nums[start]);
//NOTE: missing return statement.
Then algo fails for arrays where the target is possible to get but without using 6. eg groupSum6(0, [5, 6, 2], 7) expected value false but returns true. takes 5+2, skipps 6, but as in description every 6 must be used.
My question is: how does the 'return' statement changes the flow of recursion
Because java is pass by reference you aren't passing the actual object when you pass parameters to a method. You are passing a reference to the objects. So when you pass a reference to your objects, but take out the return call, the reference never gets resolved back to the variables, and you essentially throw away the results of your recursive call
Try this simple program to see what I mean:
public static int foo(int x) {
x++;
if(x < 10) {
foo(x);
}
return x;
}
If we call System.out.println(foo(1)); this will print out 2, since the first x++ gets resolved, but you essentially throw away the results from all your recursive calls.
However if we change
if(x < 10) {
foo(x);
}
To:
if(x < 10) {
return foo(x);
}
The result will be 10 because we return the results from the recursive calls

Java - Recursive solution for subset sum

I am told to write a recursive function that takes a start index, array of integers,and a target sum, your goal is to find whether a subset of of the array of integers adds up to the target sum.
The example I am given is groupSum(0, {2, 4, 8}, 10) should return true because 2 and 8 add up to the target, 10. All I've been able to do so far are the base cases.
public boolean groupSum(int start, int[] nums, int target) {
if (nums.length == 0)
{
return false;
}
else if (start == nums.length - 1)
{
return nums[start] == target;
}
else
{
?????
}
}
I have no idea where I should go with the actual recursive call. Since I can't pass a collective sum between calls, I don't see how I can add a number in each recursive call until I reach the target. Also, like shown in the example, I have no idea how I could have my code realize when a number won't work and just skip it, like the example did with 4. I'm thinking along the lines of that I should subtract numbers one at a time from int target and then recursively call the method with a new starting point and the new value for target, but I have no idea how I could use that to see if there is a valid subset.
I would appreciate any help that can help me understand how to do this problem so that I can finish it. Thanks!
As you point out you can change the target rather than passing a collective sum. Once the target is zero you know that you've got a solution (by selecting no members of the remaining items).
So, in psueduo code:
hasMembersThatSumTo(list, total):
if total == 0
return true
else if total < 0 or list is empty
return false
else
int first = list.pop
return hasMembersThatSumTo(list, total - first)
or hasMembersThatSumTo(list, total)
The two cases in the 'or' statement are looking for this situation in which the current element is either in or not in the sum.
Here is a working version. See comments in the code for explanation.
public static boolean recursiveSumCheck(int target, int[] set) {
//base case 1: if the set is only one element, check if element = target
if (set.length == 1) {
return (set[0] == target);
}
//base case 2: if the last item equals the target return true
int lastItem = set[set.length - 1];
if (lastItem == target) {
return true;
}
//make a new set by removing the last item
int[] newSet = new int[set.length - 1];
for (int newSetIndex = 0; newSetIndex < newSet.length; newSetIndex++) {
newSet[newSetIndex] = set[newSetIndex];
}
//recursive case: return true if the subset adds up to the target
// OR if the subset adds up to (target - removed number)
return (recursiveSumCheck(target, newSet) || recursiveSumCheck(target - lastItem, newSet));
}
dynamic programming. Maybe this will help you.
This should work according to the given conditions.
Initially the value of start = 0
boolean subsetSum(int start, int[] nums, int target) {
// target is the sum you want to find in the nums array
if (target == 0) return true;
if (start > nums.length-1 || nums.length == 0)
return false;
if (nums[start] > target)
return subsetSum(start+1, nums, target);
return subsetSum(start+1, nums, target) || subsetSum(start+1, nums, target - nums[start]);
}

Using recursion to identify an array as a MaxHeap

On this implementation of Priority Queue I have to use recursion to determinate if the array that I receive as a parameter of the method IsMaxHeap is a max heap.
I want to be sure I evaluate all the cases that could make this work or not work without any problem.
static boolean isMaxHeap(int[] H, int idx) {
if(2*idx == (H.length -1) && H[2 * idx] <= H[idx])
return true;
if(2*idx > (H.length -1))
return true;
if ((2*idx+1) == (H.length -1) && H[2 * idx] <= H[idx] && H[2 * idx + 1] <= H[idx])
return true;
if((2*idx+1) > (H.length -1))
return true;
if (H[2 * idx] <= H[idx] && H[2 * idx + 1] <= H[idx])
return isMaxHeap(H, (idx + 1));
else
return false;
}
Could you help me?
Your code is hard to follow because you do so many calculations in your conditionals. So it's hard to say whether it would actually work. Also, what you've written is basically a recursive implementation of a for loop. That is, you check nodes 1, 2, 3, 4, 5, etc.
Whereas that can work, you end up using O(n) stack space. If you have a very large heap (say, several hundred thousand items), you run the risk of overflowing the stack.
A more common way to implement this recursively is to do a depth-first traversal of the tree. That is, you follow the left child all the way to the root, then go up one level and check that node's right child, and its left children all the way to the root, etc. So, given this heap:
1
2 3
4 5 6 7
8 9 10 11 12 13 14 15
You would check nodes in this order: [1, 2, 4, 8, 9, 5, 10, 11, 3, 6, 12, 13, 7, 14, 15]
Doing it that way simplifies your code and also limits your stack depth to O(log n), meaning that even with a million nodes your stack depth doesn't exceed 20.
Since you're using 2*idx and 2*idx+1 to find the children, I'm assuming that your array is set up so that your root node is at index 1. If the root is at index 0, then those calculations would be: 2*idx+1 and 2*idx+2.
static boolean isMaxHeap(int[] H, int idx)
{
// Check for going off the end of the array
if (idx >= H.length)
{
return true;
}
// Check the left child.
int leftChild = 2*idx;
if (leftChild < H.length)
{
if (H[leftChild] > H[idx])
return false;
if (!isMaxHeap(H, leftChild)
return false;
}
// Check the right child.
int rightChild = 2*idx + 1;
if (rightChild < H.length)
{
if (H[rightChild] > H[idx])
return false;
return isMaxHeap(H, rightChild);
}
return true;
}

Java Algorithm Question

Problem Statement :: In Java ,Given an array of ints, is it possible to choose a group of some of the ints, such that the group sums to the given target, with this additional constraint: if there are numbers in the array that are adjacent and the identical value, they must either all be chosen, or none of them chosen. For example, with the array {1, 2, 2, 2, 5, 2}, either all three 2's in the middle must be chosen or not, all as a group. (one loop can be used to find the extent of the identical values).
groupSumClump(0, {2, 4, 8}, 10) → true
groupSumClump(0, {1, 2, 4, 8, 1}, 14) → true
groupSumClump(0, {2, 4, 4, 8}, 14) → false --> Failing Test Case
groupSumClump(0, {8, 2, 2, 1}, 9) → true --> Failing Test Case
groupSumClump(0, {8, 2, 2, 1}, 11) → false --> NegativeArraySizeException
I have done some initial Analysis and the partial code is as below.
public boolean groupSumClump(int start, int[] nums, int target) {
start = 0;
boolean flag = false;
// get the highest int from the list of array we have
int highestInteger = getTheBiggest(nums);
if (highestInteger > target) {
flag = false;
} else {
int variable = 0;
for (int i = 0; i < nums.length; i++) {
variable += nums[i];
}
if (variable == target) {
flag = true;
} else {
if (variable < target) {
flag = false;
} else {
// here goes ur grouping logic here
flag = summate(highestInteger, target, nums);
}
}
}
return flag;
}
private boolean summate(int highestInteger, int target, int[] nums) {
boolean val = false;
if (highestInteger == target) {
val = true;
} else {
int[] temp = new int[nums.length - 1];
int var = 0;
if ((target - highestInteger) > 0) {
for (int j = 0; j < nums.length-1; j++) {
if (nums[j] != highestInteger) {
temp[var] = nums[j];
if (temp[var] == (target - highestInteger)) {
val = true;
return val;
}
var++;
}
}
val = summate(getTheBiggest(temp), target - highestInteger,
temp);
}
}
return val;
}
private int getTheBiggest(int[] nums) {
int biggestInteger = 0;
for (int i = 0; i < nums.length; i++) {
if (biggestInteger < nums[i]) {
biggestInteger = nums[i];
}
}
return biggestInteger;
}
Please Note: I dont know how to handle the logic for below problem statement :
There is an Additional Constraint to the problem such that if there are numbers in the array that are adjacent and the identical value, they must either all be chosen, or none of them chosen. For example, with the array {1, 2, 2, 2, 5, 2}, either all three 2's in the middle must be chosen or not, all as a group. (one loop can be used to find the extent of the identical values).
how should i handle this part of logic in above problem.
I have been struggling to get this right with no idea.
Suggestions provided will be appreciated.
Culd you let me know what is the problem with the code/how to handle the additional constraint in this problem, :-((
Additional constraint says either u select as a group and not select as a group.so i dont know how to proceed.if u can PLEASE help me.it will be appreciated.
EDIT FOR USER->MISSINGNO: I have added the below code construct to above main code and it prints me wrong values.where have i gone wrong.
groupSumClump(0, {2, 4, 4, 8}, 14) → false is failing again
2
8
4
The flag is -->true which is wrong.
for(int number=0;number<nums.length-1;number++){
if(nums[number]==nums[number+1]){
nums[number]=nums[number]+nums[number+1];
}
}
I would convert the array to a simpler array that can be solved with your previous method, by clumping the adjacent values:
{1, 2, 2, 2, 5, 2} --> {1, 6, 5, 2}
You might want to keep some extra bookkeeping info though to be able to find the original solution from a solution to the altered problem.
This problem is similar to finding group of integers in an array which sum up to a given target.
Hint for this problem is:
The base case is when start>=nums.length. In that case, return true if target==0. Otherwise, consider the element at nums[start]. The key idea is that there are only 2 possibilities -- nums[start] is chosen or it is not. Make one recursive call to see if a solution is possible if nums[start] is chosen (subtract nums[start] from target in that call). Make another recursive call to see if a solution is possible if nums[start] is not chosen. Return true if either of the two recursive calls returns true.
You can use the same hint but with a loop in it to find sum of repeated numbers in array. Make a call to see if a solution is possible if the sum is chosen and make another call if the sum is not chosen.Return true if either of the two recursive calls returns true.
I think this would help.
public boolean groupSumClump(int start, int[] nums, int target)
{
if(start >= nums.length) return target == 0;
int count = 1;
while(start+count < nums.length && nums[start] == nums[start+count])
count++;
if(groupSumClump(start+count, nums, target-count*nums[start])) return true;
if(groupSumClump(start+count, nums, target)) return true;
return false;
}
You Have taken quite a lengthy approach to solving this question. Try thinking more recursively.
For the Base Case, we'll check if the start is greater than or equal to the length of the provided array(nums). If it is true, we'll check if we've reached the target sum, i.e. target is equal to 0 or not. If it is, then we'll return true as we have an answer, else return false because we have traversed the whole array, and still not reached our target.
Base Case -
if(start>=nums.length)
return target==0;`
Now, we'll take a counter variable int c=1;. We'll use it to count the number of repeating characters present(if any). How we'll do it is, we will use a while loop and iterate till nums[start] to nums[start+c] and keep on increasing the counter by 1. When this loop will end, if the value of c is 1, then we didn't find any repeating characters. Else, we have found c repeating characters of nums[start]. Make Sure you don't go out of bounds in this while loop.
int c=1;
while(start+c<nums.length&&nums[start]==nums[start+c])
c++;
Now, you know if you have repeating characters or not. You either have to include all of them or none of them. We already have c, so now we just have to call the function in which it includes c*nums[start] or it doesn't include c*nums[start]
Now, if we have repetitions, the function will skip or take all of the same values. But if we don't have repetitions, just because the value of c is 1, the function call will work in this case too. Try to dry run the code to get a better idea.
In the end, if all the function calls return false, then we know that we don't have an answer, so we too will return false.
public boolean groupSumClump(int start, int[] nums, int target) {
if(start>=nums.length)
return target==0;
int c=1;
while(start+c<nums.length&&nums[start]==nums[start+c])
c++;
if(groupSumClump(start+c,nums,target-
c*nums[start])||groupSumClump(start+c,nums,target))
return true;
return false;
}
Once you've done missingno's preprocessing step (in linear time) what you have is essentially the subset sum problem. It's a rather hard problem, but approximate algorithms exist- might as well turn out to be practical depending on how long your sequence is.
the problem statement is ambigious and not clear:
with this additional constraint: if there are numbers in the array
that are adjacent and the identical value, they must either all be
chosen, or none of them chosen. For example, with the array {1, 2, 2,
2, 5, 2}, either all three 2's in the middle must be chosen or not,
all as a group
how about choosing single digits if the 'streaks' did not work out?
same example: {1,2,2,2,5,2}
in one recursive call we choose the streak of 2 2 2 (from answer above)
*if(groupSumClump(start+count, nums, target-count*nums[start])) return true*
in the next rec. call why can't we subtract the first single digit at nums[start]:
if(groupSumClump(start+count, nums, target)) return true;
can i do this:
if(groupSumClump(start+1, nums, target - nums[start])) return true;
DOES IT MEAN WE CAN NEVER CHOOSE SINGLE DIGITS?
Your public method parameter list for the method groupSumClump should not expose the start parameter, especially if you have an internal variable that overrides it.
Have a look at this implementation. The time complexity for the solve method is O(2^N), since in the worst case you are obliged to check all possible subsets of nums. Well unless someone proves that NP = P.
I Hope it helps.
import java.util.*;
public class SubsetSumSolver {
public boolean solve(int[] nums, int target) {
return solve(0, 0, target, mergeSameNumbers(nums));
}
private boolean solve(int start, int tempSum, int sum, ArrayList<Integer> nums) {
if (start == nums.size())
return sum == tempSum;
return solve(start + 1, tempSum + nums.get(start),sum, nums) || solve(start + 1, tempSum, sum, nums);
}
private ArrayList<Integer> mergeSameNumbers(int[] nums) {
if (nums.length == 0)
return toArrayList(nums);
LinkedList<Integer> merged = new LinkedList<>();
int tempSum = nums[0];
for (int i = 1; i < nums.length; i++) {
if (nums[i - 1] == nums[i])
tempSum += nums[i];
else {
merged.add(tempSum);
tempSum = nums[i];
}
}
merged.add(tempSum);
return new ArrayList(merged);
}
private ArrayList<Integer> toArrayList(int[] nums) {
ArrayList<Integer> result = new ArrayList();
for (int index = 0; index < nums.length; index++)
result.add(nums[index]);
return result;
}
}
My solution with a HasMap.
public boolean groupSumClump(int start, int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Arrays.sort(nums);
for (int i : nums) {
if (!map.containsKey(i)) {
map.put(i, 1);
}
else {
map.put(i, map.get(i) + 1);
}
}
return groupSumClumpHelper(start, nums, target, map);
}
private boolean groupSumClumpHelper(int start, int[] nums, int target, Map map) {
if (start >= nums.length) {
return target == 0;
}
if (!map.get(nums[start]).equals(1)) {
return groupSumClumpHelper(start + Integer.parseInt(map.get(nums[start]).toString()), nums, target - Integer.parseInt(map.get(nums[start]).toString()) * nums[start], map) ||
groupSumClumpHelper(start + Integer.parseInt(map.get(nums[start]).toString()), nums, target, map);
}
return groupSumClumpHelper(start + 1, nums, target - nums[start], map) || groupSumClumpHelper(start + 1, nums, target, map);
}

Categories

Resources