How can I solve this problem with recursion in Java? - java

public static int[] generateNumbers(int start, int end) {
// case for empty array
if (start > end) {
int returnEmptyArray[] = {};
return returnEmptyArray;
}
// setting size of the result array
if (start < 0) {
int sizeOfArray = end - start - 1;
int result[] = new int[sizeOfArray];
}
// setting size of the result array
if (start > 0) {
int sizeOfArray = end - start;
int result[] = new int[sizeOfArray];
return generateNumbers(start, end, 0, result);
}
return null;
}
// helping method for recursion
public static int[] generateNumbers(int start, int end, int i, int[] result) {
i = start;
if (i < end) {
result[i] = i;
i++;
}
return generateNumbers(start, end, ++i, result);
}
Hi everybody!
I have tried everything I could imagine to solve this problem.
This program should count upwards recursively from start to end and negative numbers should be included as well. In the end, the result should be put into an integer array.
Examples:
start: 2, end: 5 --> result = {2, 3, 4, 5}
start: -4, end: 3 --> result = {-4, -3, -2, -1, 0, 1, 2, 3}
I would be really thankful for a code snippet and an explanation or at least an approach how I can solve this problem.

There are some quite odd parts in your code.
For example this:
if (start < 0) {
int sizeOfArray = end - start - 1;
int result[] = new int[sizeOfArray];
}
is pointless, you are creating local variables in if condition and never use them.
Your calculation of the sizeOfArray is wrong, it should be end - start + 1, also, there is no reason to check whether start is positive or negative, ditch it all. Then, remove return null, it's a dead code.
For your subroutine function, you can't just write into array on the same index as the number you are about to write, you will get ArrayIndexOutOfBoundsException. For example if your start is -4, you will basically (in the first call) attempt to do result[-4] = -4, which is obviously nonsence.
Better solution would be to hold index value in i (starting from 0) and adding this value to start: result[i] = start + i.
Also, the ++i call is wrong, as you call i++ right before it, so you are actually increasing i by 2 instead of 1.
Your subroutine also has no terminating condition - it can never end. Try to put it in format:
if(...) {
...
return generateNumbers(...);
}
return result;
Btw, you will obviously need to edit your condition after changing what i does.

Instead of giving you a solution, I'll go through the reasoning how to find the solution.
[ We all fully understand that creating such an array of integers this way is meant as an exercise in recursive thinking, not to be done this way in production-quality software. ]
The main two questions for recursion are always:
For a given task, like generateNumbers(3,7), how can I solve it by re-using the results of a "simpler" task of the same type?
What is a task so simple that I want to solve it directly? And I should choose a case where I'll surely arrive when applying my simplification strategy from question 1.
One possible answer is (there are other possibilities):
If I know the result of generateNumbers(3,6), I just have to append the 7 at the end, and then I got it. Generalized: Solving the question generateNumbers(start,end-1), and then appending end.
If start is greater than end, the result is an empty array.
Implementing that is straightforward, with one quirk: appending something to an array isn't easy in Java. You can:
Make the recursion more complicated, by first creating an array long enough to hold the final result, and in the recursive calls keep track of the position where to append the new number.
Use the List data type instead of an array (if your course has covered that). Appending to a List is easy.
Write an int[] append(int[] arraySoFar, int newValue) method, thus making the "append" step in the recursion easy. The method has to create a new array, one longer than the input, copy the input there, and insert the new value at the end.
Just to show you that there can be many different ways of using resursion for your problem, here are a few other approaches:
Instead of reducing the end by one, and then appending the end number, you can increase the start by one and prepend the start number.
For solving generateNumbers(3,7), you can find some number in the middle, e.g. 5, and solve by appending generateNumbers(3,5) and generateNumbers(6,7), meaning that you create the result of one generateNumbers() call by cleverly combining the results of two recursive calls.

public static int[] generateNumbers(int start, int end) {
int length = end - start + 1;
if(length <= 0) {
// throw an exception
}
int[] numbers = new int[length];
generateNumbers(start, end, 0, numbers);
return numbers;
}
public static void generateNumbers(int start, int end, int index, int[] numbers) {
if(start > end) {
return;
}
numbers[index] = start;
generateNumbers(start + 1, end, index + 1, numbers);
}

Related

Circular Array Loop, detection

I am working on a problem, and have spent some time on it.
Problem statement:
You are given an array of positive and negative integers. If a number n at an index is positive, then move forward n steps. Conversely, if it's negative (-n), move backward n steps. Assume the first element of the array is forward next to the last element, and the last element is backward next to the first element. Determine if there is a loop in this array. A loop starts and ends at a particular index with more than 1 element along the loop. The loop must be "forward" or "backward'.
Example 1: Given the array [2, -1, 1, 2, 2], there is a loop, from index 0 -> 2 -> 3 -> 0.
Example 2: Given the array [-1, 2], there is no loop.
Note: The given array is guaranteed to contain no element "0".
Can you do it in O(n) time complexity and O(1) space complexity?
And this is my solution in progress, however, I am not sure how should I end the do-while condition, when there is no loop detected. I believe my code will run infinitely if there is no loop detected.
public static boolean circularArrayLoop(int[] nums) {
int size = nums.length;
if(size < 2) return false;
int loopStart = nums[0];
int index = 0;
int start = nums[0];
do{
if(nums[index] > 0){
index = moveForward(index, nums[index], size);
}else {
index = moveBackward(index, Math.abs(nums[index]), size);
}
}while (loopStart != nums[index]);
}
This can be seen as a version of cycle detection in a directed (possibly disconnected) graph or more like finding a minimum spanning trees for all the connected subgraphs in the given graph. The numbers in the array are vertices and an edge will be formed between the vertices based on the vertice value. There are no known graph parsing algorithms which can possibly solve it in O(1) space complexity. This might be solved in O(n) time complexity as the best graph parsing algorithms can be solved in O(V+E) time and V=E in this case which makes it possible to solve with O(n) time complexity in some cases. The best-known algorithm is Kruskal's: http://www.geeksforgeeks.org/greedy-algorithms-set-2-kruskals-minimum-spanning-tree-mst/ which solves in O(nlogn) time.
Since there are guaranteed no elements with value 0, there is always going to be a loop. The qualifier is loops must be greater than a single element long.
With this condition, when advancing to the next index as directed by the array element value results in the same index being reached, "no" loop is present.
The fast and slow moving cursors can be used to find the beginning of the loop. Then advancing a single cursor until it returns to the same index would let you iterate over the elements of the loop. If a single advancement returns the cursor to the same index no loop is present.
public static void main(String[] args) {
int[] loop = {2, -1, 1, 2, 2};
int[] noloop = {-1, 2};
System.out.println(circularArrayLoop(loop));
System.out.println(circularArrayLoop(noloop));
}
static int nextIndex(int[] nums, int cur) {
// Get next index based on value taking into account wrapping around
}
static boolean circularArrayLoop(int[] nums) {
int fast = 0;
int slow = 0;
do {
// advance fast cursor twice
// advance slow cursor once
} while (fast != slow);
int next = nextIndex(nums, fast);
// return if the loop has more than a single element
}
Am I wrong to think there is no guarantee that the loop will go on with the first element ? Thus, you can't just do int loopStart = nums[0];
What if your example 1 was rather [2, -1, 1, 4, 2], then the loop would be from index 0 -> 2 -> 3 -> 2. And, your check with loopstart wouldn't work, since it checks sums[0].
A good solution is to use 2 variables and move them at different speed (one twice the speed). If the array/linked list is circular, you'll get to a point where var1 equals var2.
Here's the pseudocode:
if array.length<=1
return false
int i=0;
//loop is when "var1 == var2"
//end is when "var1 == abs(array.length)"
loop (until var1 == var2 or var1 reaches the end)
var1 = moveToNext(var1)
if (i++ % 2 == 0)
var2 = moveToNext(var2)
return var1 == var2;
This is quite similar to a question generally asked using linked list: How to detect a loop in a linked list?

Tough recursive task

I've been struggle with question I'm trying to solve as part of test preparation, and I thought I could use your help.
I need to write a Boolean method that takes array with integers (positive and negative), and return true if the array can be split to two equals groups, that the amount of every group's numbers is equals to the other group.
For exmaple, for this array:
int[]arr = {-3, 5, 12, 14, -9, 13};
The method will return true, since -3 + 5 + 14 = 12 + -9 + 13.
For this array:
int[]arr = {-3, 5, -12, 14, -9, 13};
The method will return false since even though -3 + 5 + 14 + -12 = -9 + 13, the amount of numbers in every side of the equation isn't equals.
For the array:
int[]arr = {-3, 5, -12, 14, -9};
The method will return false since array length isn't even.
The method must be recursive, overloading is allowed, every assist method must be recursive too, and I don't need to worry about complexity.
I've been trying to solve this for three hours, I don't even have a code to show since all the things I did was far from the solution.
If someone can at least give me some pseudo code it will be great.
Thank you very much!
You asked for pseudocode, but sometimes it's just as easy and clear to write it as Java.
The general idea of this solution is to try adding each number to either the left or the right of the equation. It keeps track of the count and sum on each side at each step in the recursion. More explanation in comments:
class Balance {
public static void main(String[] args) {
System.out.println(balanced(-3, 5, 12, 14, -9, 13)); // true
System.out.println(balanced(-3, 5, -12, 14, -9, 13)); // false
}
private static boolean balanced(int... nums) {
// First check if there are an even number of nums.
return nums.length % 2 == 0
// Now start the recursion:
&& balanced(
0, 0, // Zero numbers on the left, summing to zero.
0, 0, // Zero numbers on the right, summing to zero.
nums);
}
private static boolean balanced(
int leftCount, int leftSum,
int rightCount, int rightSum,
int[] nums) {
int idx = leftCount + rightCount;
if (idx == nums.length) {
// We have attributed all numbers to either side of the equation.
// Now check if there are an equal number and equal sum on the two sides.
return leftCount == rightCount && leftSum == rightSum;
} else {
// We still have numbers to allocate to one side or the other.
return
// What if I were to place nums[idx] on the left of the equation?
balanced(
leftCount + 1, leftSum + nums[idx],
rightCount, rightSum,
nums)
// What if I were to place nums[idx] on the right of the equation?
|| balanced(
leftCount, leftSum,
rightCount + 1, rightSum + nums[idx],
nums);
}
}
}
This is just a first idea solution. It's O(2^n), which is obviously rather slow for large n, but it's fine for the size of problems you have given as examples.
The problem described is a version of the Partition problem. First note that your formulation is equivalent to deciding whether there is a subset of the input which sums up to half of the sum of all elements (which is required to be an integral number, otherwise the instance cannot be solved, but this is easy to check). Basically, in each recursive step, it is to be decided whether the first number is to be selected into the subset or not, resulting in different recursive calls. If n denotes the number of elements, there must be n/2 (which is required to be integral again) items selected.
Let Sum denote the sum of the input and let Target := Sum / 2 which in the sequel is assumed to be integral. if we let
f(arr,a,count) := true
if there is a subset of arr summing up to a with
exactly count elements
false
otherwise
we obtain the following recursion
f(arr,a,count) = (arr[0] == a && count == 1)
||
(a == 0 && count == 0)
if arr contains only one element
f(arr\arr[0], a, count)
||
f(arr\arr[0], a - arr[0], count -1)
if arr contains more than one element
where || denotes logical disjuction, && denoted logical conjunction and \ denotes removal of an element.
The two cases for a non-singleton array correspond to chosing the first element of arr into the desired subset or its relative complement. Note that in an actual implementation, a would not be actually removed from the array; a starting index, which is used as an additional argument, would be initialized with 0 and increased in each recursive call, eventually reaching the end of the array.
Finally, f(arr,Target,n/2) yields the desired value.
Your strategy for this should be to try all combinations possible. I will try to document how I would go about to get to this.
NOTE that I think the requirement: make every function use recursion is a bit hard, because I would solve that by leaving out some helper functions that make the code much more readable, so in this case I wont do it like that.
With recursion you always want to make progression towards a final solution, and detect when you are done. So we need two parts in our function:
The recursive step: for which we will take the first element of the input set, and try what happens if we add it to the first set, and if that doesn't find a solution we'll try what happens when we add it to the second set.
Detect when we are done, that is when the input set is empty, in that case we either have found a solution or we have not.
A trick in our first step is that after taking the first element of our set, if we try to partition the remainder, we don't want the 2 sets being equal anymore, because we already assigned the first element to one of the sets.
This leads to a solution that follows this strategy:
public boolean isValidSet(MySet<int> inputSet, int sizeDifferenceSet1minus2)
{
if (inputSet.isEmpty())
{
return sizeDifferenceSet1minus2== 0;
}
int first = inptuSet.takeFirst();
return isValidSet(inputSet.copyMinusFirst(), sizeDifferenceSet1minus2+ first)
|| isValidSet(inputSet.copyMinusFirst(), sizeDifferenceSet1minus2+ -1 * first);
}
This code requires some help functions that you will still need to implement.
What it does is first test if we have reached the end condition, and if so returns if this partition is successful. If we still have elements left in the set, we try what happens if we add it to the first set and then what happens when adding it to the second set. Note that we don't actually keep track of the sets, we just keep track of the size difference between set 1 minus 2, decreasing the (but instead you could pass along both sets).
Also note that for this implementation to work, you need to make copies of the input set and not modify it!
For some background information: This problem is called the Partition Problem, which is famous for being NP-complete (which means it probably is not possible to solve it efficiently for large amounts of input data, but it is very easy to verify that a partitioning is indeed a solution.
Here is a verbose example:
public static void main(String[] args)
{
System.out.println(balancedPartition(new int[] {-3, 5, 12, 14, -9, 13})); // true
System.out.println(balancedPartition(new int[] {-3, 5, -12, 14, -9, 13})); // false
System.out.println(balancedPartition(new int[] {-3, 5, -12, 14, -9})); // false
}
public static boolean balancedPartition(int[] arr)
{
return balancedPartition(arr, 0, 0, 0, 0, 0, "", "");
}
private static boolean balancedPartition(int[] arr, int i, int groupA, int groupB, int counterA, int counterB, String groupAStr, String groupBStr)
{
if (groupA == groupB && counterA == counterB && i == arr.length) // in case the groups are equal (also in the amount of numbers)
{
System.out.println(groupAStr.substring(0, groupAStr.length() - 3) + " = " + groupBStr.substring(0, groupBStr.length() - 3)); // print the groups
return true;
}
if (i == arr.length) // boundaries checks
return false;
boolean r1 = balancedPartition(arr, i + 1, groupA + arr[i], groupB, counterA + 1, counterB, groupAStr + arr[i] + " + ", groupBStr); // try add to group 1
boolean r2 = balancedPartition(arr, i + 1, groupA, groupB + arr[i], counterA, counterB + 1, groupAStr, groupBStr + arr[i] + " + "); // try add to group 2
return r1 || r2;
}
Output:
-3 + 5 + 14 = 12 + -9 + 13 // one option for the first array
12 + -9 + 13 = -3 + 5 + 14 // another option for the first array
true // for the first array
false // for the second array
false // for the third array

algorithm to get subset of array with target sum is not giving working

The problem is given an unsorted array, give subsets of array that can produce target sum:
For eg:
target = 15
data = {3,4,5,7,1,2,9};
Expected results (note the results are sorted for simplicity. not a requirement) :
[1, 2, 3, 4, 5]
[1, 2, 3, 9]
[1, 2, 5, 7]
[1, 3, 4, 7]
[1, 5, 9]
[2, 4, 9]
[3, 5, 7]
Here is my naive approach to this problem - simple and brute force.
public static void naiveSubset(int[] arr, int target){
int sum=0;
List<Integer> result = new ArrayList<>();
for (int i=0; i< arr.length;i++){
sum =arr[i];
result.add(arr[i]);
for (int j=0;j<arr.length;i++){
if (sum==target){
System.out.println(result);
result.clear();
break;
}
else if (i!=j && sum+arr[j] <= target){
sum+=arr[j];
result.add(arr[j]);
}
}
}
}
For some reasons, I am not expecting the results. I tried browsing through the code to dig out any issues. But I could not find any. please algo experts, point me in correct direction!!
The results I get (for same input as above)
[3, 3, 3, 3, 3]
[9, 3, 3]
Your solution is wrong because it's a greedy approach. It decides if you should add a number or not based on the fact that adding it does not violate the sum, at the moment.
However, this greedy approach does not work, with a simple example of the following array: [1,9,6,5] and with sum=11.
Note that for any element you choose in the outer loop, next you will add 1 to the current set. But that will deny you the possibility to get the sum of 5+6.
Once you choose 5, you start adding number, starting with '1', and adding it. Once it is added - you will never get the correct solution.
Also note: Your double loop approach can generate at most O(n^2) different subsets, but there could be exponential number of subsets - so something must be wrong.
If you want to get all possible subsets that sum to the given sum, you can use a recursive solution.
At each step "guess" if the current element is in the set or not, and recurse for both options for the smaller problem - if the data is in the set, or if it's not.
Here is a simple java code that does it:
public static void getAllSubsets(int[] elements, int sum) {
getAllSubsets(elements, 0, sum, new Stack<Integer>());
}
private static void getAllSubsets(int[] elements, int i, int sum, Stack<Integer> currentSol) {
//stop clauses:
if (sum == 0 && i == elements.length) System.out.println(currentSol);
//if elements must be positive, you can trim search here if sum became negative
if (i == elements.length) return;
//"guess" the current element in the list:
currentSol.add(elements[i]);
getAllSubsets(elements, i+1, sum-elements[i], currentSol);
//"guess" the current element is not in the list:
currentSol.pop();
getAllSubsets(elements, i+1, sum, currentSol);
}
Note that if you are looking for all subsets, there could be exponential number of those - so an inefficient and exponential time solution is expected.
If you are looking for finding if such a set exist, or finding only one such set, this can be done much more efficiently using Dynamic Programming. This thread explains the logic of how it can be done.
Note that the problem is still NP-Hard, and the "efficient" solution is actually only pseudo-polynomial.
I think the major issue in your previous approach is that simply doing loops based upon the input array will not cover all the combinations of numbers matching the target value. For example, if your major loop is in ith, and after you iterate through the jth element in your secondary loop, your future combination based on what you have collected through ith element will never include jth one anymore. Intuitively speaking, this algorithm will collect all the visible combinations through numbers near each other, but not far away from each other.
I wrote a iterative approach to cope with this subset sum problem through C++ (sorry, not have a java environment at hand:P), the idea is basically the same as the recurrsive approach, which means you would record all the existing number combinations during each iteration in your loop. I have one vector<vector> intermediate used to record all the encountered combination whose value is smaller than target, and vector<vector> final used to record all the combinations whose sum is equal to target.
The detailed explanation is recorded inline:
/* sum the vector elements */
int sum_vec(vector<int> tmp){
int sum = 0;
for(int i = 0; i < tmp.size(); i++)
sum += tmp[i];
return sum;
}
static void naiveSubset(vector<int> arr, int target){
/* sort the array from big to small, easier for us to
* discard combinations bigger than target */
sort(arr.begin(), arr.end(), greater<int>());
int sum=0;
vector<vector<int> > intermediate;
vector<vector<int> > final;
for (int i=0; i< arr.size();i++){
int curr_intermediate_size = intermediate.size();
for(int j = 0; j < curr_intermediate_size; j++){
int tmpsum = sum_vec(intermediate[j]);
/* For each selected array element, loop through all
* the combinations at hand which are smaller than target,
* dup the combination, put it into either intermediate or
* final based on the sum */
vector<int> new_comb(intermediate[j]);
if(tmpsum + arr[i] <= target){
new_comb.push_back(arr[i]);
if(tmpsum + arr[i] == target)
final.push_back(new_comb);
else
intermediate.push_back(new_comb);
}
}
/* finally make the new selected element a separate entry
* and based on its value, to insert it into either intermediate
* or final */
if(arr[i] <= target){
vector<int> tmp;
tmp.push_back(arr[i]);
if(arr[i] == target)
final.push_back(tmp);
else
intermediate.push_back(tmp);
}
}
/* we could print the final here */
}
Just wrote it so please bear with me if there is any corner case that I did not consider well. Hope this helps:)

How to get partition of quicksort to produce the correct and expected output?

This concerns "a software algorithm" from https://stackoverflow.com/help/on-topic, in this case the quicksort sorting algorithm
This is a practice coding question(non competition) from https://www.hackerrank.com/challenges/quicksort1
Basically you're supposed to take in a list, say
4 5 3 7 2
and partition around the first element, in this case 4. The expected output is
3 2 4 5 7
However the output I am getting is
2 3 4 7 5
Here is my code for doing the partitioning(based off the learning friendly version from https://courses.cs.washington.edu/courses/cse373/13wi/lectures/02-27/20-sorting3-merge-quick.pdf slide 16)
static void partition(int[] ar) {
int toPartitionAround = ar[0];
swap(ar, 0, ar.length - 1);
int index = partition(ar, 0, ar.length - 2, toPartitionAround);
swap(ar, index, ar.length - 1);
printArray(ar);
}
static int partition(int[] ar, int left, int right, int toAround) {
while(left <= right) {
while(ar[left] < toAround) {
left ++;
}
while(ar[right] > toAround) {
right --;
}
if(left <= right) {
swap(ar, left, right);
left ++;
right --;
}
}
return left;
}
static void swap(int[] arrayNums, int index1, int index2) {
int temp = arrayNums[index1];
arrayNums[index1] = arrayNums[index2];
arrayNums[index2] = temp;
}
Is there a modification I can make to this to match the expected output? Am I technically still getting a possible correct output because my output does follow the property elements to the left of the pivot are less than the pivot and elements to the right of the pivot are greater than the pivot?
What I do is assign the pivot, move it to the back of the array and then partition the rest of the array around the pivot (from index of 0 to length - 2). With this input, one swap happened when going through the array(3 and the 5). Then when I get index from the result of the partition, I swap the pivot with that result, meaning I swap 4 and 5.
Do I follow a similar process to get the expected output? I can't see what to replace.
After reading #greybeard's and #Smac89's comments, I realized I needed a stable form of quicksort, stable as in "preserving the original order of the input set, where the comparison algorithm does not distinguish between two or more items"(https://softwareengineering.stackexchange.com/questions/247440/what-does-it-mean-for-a-sorting-algorithm-to-be-stable)
To me a stable sort doesn't really make sense for this run because the comparison algorithm does distinguish between 3 and 2, but oh well. Can someone clarify this?
The solution I came up with to maintain relative was creating two ArrayLists, one for holding values less than or equal to the pivot and one for holding values greater than the pivot. This way relative positioning is preserved.(2 will come after the 3, just like in the original list). Then I move the values from the two ArrayLists along with the pivot back into the original pivot.
Here is my solution in code if anyone is having troubles with this
static void partition(int[] ar) {
List<Integer> smallerValues = new ArrayList<Integer>();
List<Integer> biggerValues = new ArrayList<Integer>();
int toPartitionAround = ar[0];
for(int c=1;c<ar.length;c++) {
if(ar[c] <= toPartitionAround) {
smallerValues.add(ar[c]);
} else {
biggerValues.add(ar[c]);
}
}
for(int c=0;c<smallerValues.size();c++) {
ar[c] = smallerValues.get(c);
}
ar[smallerValues.size()] = toPartitionAround;
for(int m=0;m<biggerValues.size();m++) {
ar[m + smallerValues.size() + 1] = biggerValues.get(m);
}
printArray(ar);
}
That's a little confusing one isn't it. All I did to solve that question was create 2 separate lists, one to store elements lower than pivot and another to store elements higher than the pivot. Then, you just print out the values. This is the way preserve the order of elements and you do an "in-place" sorting. But it consumes memory as well.

Linked list recursion in Java

I have to code a recursive method that iterates through a linked list and returns the number of integers that are positive. Here is the question:
The method countPos below must be a recursive method that takes a Node head
as its argument, goes down the list headed by head, and counts the number of nodes which have a positive data field.
The code I have works however, I don't understand how it works.
public int countPos(Node head) {
int count = 0;
if (head == null) { return count; }
if (head.data > 0) {
count++;
return count + countPos(head.next);
} else {
return count + countPos(head.next);
}
}
The problem I'm having is I don't understand how count doesn't get set back to 0 every time the method is called. For some reason the statement int count = 0; is ignored the next time the method gets called. Is this because I'm returning count also? Any explanation would be greatly appreciated.
Thanks.
DON'T begin by tracing execution or debugging. The power of recursion is that it lets you reason about complicated programs with simple logic.
Your code works by chance. It reflects that whoever wrote it (you?) doesn't understand how recursion solves problems. It's more complex than necessary.
To exploit recursion, take the problem at hand and:
Define the function interface.
Split the problem into parts, at least one of which is a smaller version of the same problem.
Solve that (or those) smaller version(s) by calling the function interface itself.
Find the "base case" or cases that are solutions to very small instances of the same problem.
With all this done, the pseudocode for most recursive algorithms is:
function foo(args)
if args describe a base case
return the base case answer.
solve the smaller problem or problems by calling foo with
args that describe the smaller problem!
use the smaller problem solution(s) to get the answer for this set of args
return that answer
end
Let's apply this to your case:
PROBLEM: Count the number of positive items in a list.
Define the function interface: int countPos(Node head).
Split the problem up into parts: Get the number of positives in the list remaining after the head, then add one if the head is positive and nothing if the head is zero or negative.
The smaller version of the problem is finding the number of positives in the list with head removed: countPos(head.next).
Find the base case: The empty list has zero positives.
Put this all together:
int countPos(Node head) {
// Take care of the base case first.
if (head == null) return 0;
// Solve the smaller problem.
int positiveCountWithoutHead = countPos(head.next);
// Now the logic in step 2. Return either the positive count or 1+ the positive count:
return head.data > 0 ? positiveCountWithoutHead + 1 : positiveCountWithoutHead;
}
You might learn a little bit by tracing execution of something like this one time. But trying to write recursive code by reasoning about what's going on with the stack is a dead end. To be successful, you must think at a higher level.
Let's try one that doesn't quite follow the standard template: Recursive binary search. We have an array a of integers and are trying to find the index of x if it exists in the array and return -1 if not.
PROBLEM: Search the array between positions i0 and i1-1.
(The above is an example of how you must sometimes "specialize" the problem by adding parameters so that smaller subproblems can be described in the recursive call or calls. Here we are adding the new parameters i0 and i1 so that we can specify a subarray of a. Knowing how and when to do this is a matter of practice. The parameters needed can vary with language features.)
Function interface: int search(int [] a, int x, int i0, int i1)
Split the problem in parts: We'll pick a "middle" element index: mid = (i0 + i1) / 2. Then the subproblem is either searching the first half of the array up to but excluding mid or the second half of the array starting after mid and continuing to the end.
The calls are search(a, x, i0, mid) and search(a, x, mid + 1, i1).
The base cases are that 1) if i0 >= i1, there are no elements to search, so return -1 and 2) if we have a[mid] == x, then we've found x and can return mid.
Putting this all together
int search(int [] a, int x, int i0, int i1) {
// Take care of one base case.
if (i0 >= i1) return -1;
// Set up mid and take care of the other base case.
int mid = (i0 + i1) / 2;
if (a[mid] == x) return mid;
// Solve one or the other subproblems. They're both smaller!
return x < a[mid] ? search(a, x, i0, mid) : search(a, x, mid + 1, i1);
}
And to start the search:
int search(int [] a, int x) { return search(a, x, 0, a.length); }
Each time you call countPos(), a new version of that function starts. This function starts from a clean slate meaning all of the local variables (count) are its own, and no other "copy" of countPos can see or modify its local variables.
The only state that is passed between these "copies" or of countPos is the variables that are passed in as parameters (Node head).
So here's a rough workflow assuming the list [1, -2, 3]
countPos starts, and says number of positive nodes is equal to 1, since "1" is positive. The total number of positive nodes is equal to 1 + whatever the next function returns.
The next function says the number of positive nodes is equal to 0 + whatever the next function returns.
The next function says the number of positive nodes is equal to 1 + whatever the next function returns
The next function sees that head == null and so returns 0.
Now each recursive function returns one after another to the original function that called it, with the total number of positive nodes "snowballing" as we return.
The total number returned in the end will be 2.

Categories