CHAPTER 23-1
RECURSION
A simple recursive method
A recursive method is a method, when executing, calls itself either directly or indirectly.
//Example 23-1-1
class Demo
{
public static void main(String[] args)
{
System.out.println("hello");
main(args);
}
}
//line 6
//line 7
Recursive definition
Let us define the concept of a positive integer by the following definition:
A number is a positive integer if it is in the set {1, 2, 3, 4, …}
Let us consider the following definition for a positive integer:
x is a positive integer if x-1 is a positive integer
To define an object X recursively means that we must define X in terms of X. However, to have a
sound recursive definition, we must make sure that our recursive definition has both of the following
properties:
1. One or more cases where X is defined in terms of a "smaller" X. This is the recursive case for the
recursive definition.
2. One or more cases where X is defined without X. This is the base case for the recursive
definition.
The base case allows us to readily tell whether a given object is X without resorting to the recursive
case. The reason we can tell whether a given object is X without resorting to the recursive case is
because we are given a "simple" or "small" object. When we are given a "not so simple" or "not so
small" object, the base case cannot help us to identify whether the object is X. We must then use the
recursive case to successively reduce the object to a "smaller" object. If we have made enough
recursive calls, we will arrive at an object small enough for us to apply the base case.
Let us redefine positive integer using the following recursive definition:
1. Recursive case, when x>1: x is a positive integer if x-1 is a positive integer.
2. Base case, when x=1:
1 is a positive integer.
Thinking recursively
To define an operation O recursively, the recursive definition should have both of the following:
1. One or more recursive cases where O is defined in terms of O with a "smaller" operand (input).
2. One or more base cases where O is defined without O.
The base case allows us to carry out O directly without resorting to the recursive case. The reason we
can carry out O directly is because we are given a "simple" or "small" input. When we are given a "not
so simple" or "not so small" input, we do not know how to carry out O directly. We must then use the
recursive case to successively reduce the input to a "smaller" input. If we have made enough recursive
calls, we will arrive at an input small enough for us to apply the base case.
Consider the problem of printing a message n times, denoted by print(message, n).
o Here is a recursive definition:
1. Recursive case, when n>1:
print(message, n)= print(message, n)
2. Base case, when n=1.
print(message, n)= print message once
o Here is another recursive definition:
1. Recursive case, when n>1:
print(message, n)= print(message, n-1)
2. Base case, when n=1.
print(message, n)= print message once
o Here is another recursive definition:
1. Recursive case, when n>1:
print(message, n)=
print message once
print(message, n-1)
2. Base case, when n=1.
print(message, n)=
print message once
In a recursive method, we typically have an if construct. The ThenClasue of the if construct typically
implements the base case, while the ElseClause implements the recursive case.
//Example 23-1-2
class Demo
{
public static void main(String[] args)
{
print("Recursion is fun", 5);
}
public static void print(String s, int n)
{
if (n==1) System.out.println(s);
else
{
System.out.println(s);
print(s, n-1);
}
}
}
Sometimes we can combine the base case and the recursive case into 1 simple if construct. For
example, we can simplify the print method of Example 23-1-2 as follows:
public static void print(String s, int n)
{
if (n>=1)
{
System.out.println(s);
print(s, n-1);
}
}
A recursive method - sum
We want to find the sum of all the integers from 1 to some upper limit n. Let us denote this operation
by sum(n). How can we formally define sum(n) recursively?
//Example 23-1-3
import java.util.Scanner;
class Demo
{
public static void main(String[] args)
{
Scanner keyboard=new Scanner(System.in);
int n=0, answer=0;
System.out.print("Enter positive integer: ");
n=keyboard.nextInt();
answer=sum(n);
System.out.println("Sum is "+answer);
}
public static int sum(int n)
{
//line 7
//line 8
//line 9
//line 10
//line 11
}
}
A recursive method - factorial
//Example 23-1-4
import java.util.Scanner;
class Demo
{
public static void main(String[] args)
{
Scanner keyboard=new Scanner(System.in);
int n=0, answer=0;
System.out.print("Enter non-negative integer: ");
n=keyboard.nextInt();
answer= factorial (n);
System.out.println("factorial is "+answer);
}
public static int factorial (int n)
{
if ((n==0)||(n==1)) return 1;
else return n* factorial (n-1);
}
}
//line 7
//line 8
//line 9
//line 10
//line 11
//line 12
//line 15
//line 16
Walkthrough:
After line 7 is done (args and keyboard not shown):
main
n=0
answer=0
RA=JVM
After line 8 is done, "Enter non-negative integer: " printed on the screen.
After line 9 is done (user enters 4):
main
n=4
answer=0
RA=JVM
Now line 10 executes. The method call factorial (n) executes first. After the method call is done:
main
n=4
answer=0
RA=JVM
factorial
n=4
RA=Line 10
RV=?
Now line 15 executes. Since (n==0)||(n==1) returns false, ElseClause in line 16 executes next.
To evaluate n*factorial (n-1), the computer first evaluates n, which is 4. Then it evaluates factorial(n1). After the method call is done:
main
factorial
factorial
n=4
n=4
n=3
answer=0
RA=Line 10
RA=Line 16
RA=JVM
RV=?
RV=?
Now line 15 executes. Since (n==0)||(n==1) returns false, ElseClause in line 16 executes next.
To evaluate n*factorial (n-1), the computer first evaluates n, which is 3. Then it evaluates factorial(n1). After the method call is done:
main
factorial
factorial
factorial
n=4
n=4
n=3
n=2
answer=0
RA=Line 10
RA=Line 16
RA=Line 16
RA=JVM
RV=?
RV=?
RV=?
Now line 15 executes. Since (n==0)||(n==1) returns false, ElseClause in line 16 executes next.
To evaluate n*factorial (n-1), the computer first evaluates n, which is 2. Then it evaluates factorial(n1). After the method call is done:
main
factorial
factorial
factorial
factorial
n=4
n=4
n=3
n=2
n=1
answer=0
RA=Line 10
RA=Line 16
RA=Line 16
RA=Line 16
RA=JVM
RV=?
RV=?
RV=?
RV=?
Now line 15 executes Since (n==0)||(n==1) returns true, ThenClause in line 15 executes next. Thus the
expression (return 1) executes. After the return expression is done, the activation record of the most
recent activated factorial is destroyed (except the return value cell), and control return to line 16 of its
caller:
main
factorial
factorial
factorial
n=4
n=4
n=3
n=2
answer=0
RA=Line 10
RA=Line 16
RA=Line 16
RA=JVM
RV=?
RV=?
RV=?
RV=1
Now, line 16 resumes. Recall line 16 is "return n* factorial (n-1);". Now that factorial(n-1) has
returned with the return value of 1, the expression n*factorial(n-1) proceeds and evaluates to 2. Then
the return statement executes. After the return expression is done:
main
factorial
factorial
n=4
n=4
n=3
answer=0
RA=Line 10
RA=Line 16
RA=JVM
RV=?
RV=?
RV=2
Now, line 16 resumes. Now that factorial(n-1) has returned with the return value of 2, the expression
n*factorial(n-1) proceeds and evaluates to 6. Then the return statement executes. After the return
expression is done:
main
factorial
n=4
n=4
answer=0
RA=Line 10
RA=JVM
RV=?
RV=6
Now, line 16 resumes. Now that factorial(n-1) has returned with the return value of 6, the expression
n*factorial(n-1) proceeds and evaluates to 24. Then the return statement executes. After the return
expression is done:
main
n=4
answer=0
RA=JVM
RV=24
Now, line 10 resumes. Recall line 10 is "answer= factorial (n);". Now that factorial(n) has returned
with the return value of 24, the assignment proceeds. After line 10 is done:
main
n=4
answer=24
RA=JVM
Now, line 11 executes. After line 11 is done, "factorial is 24" is printed on the screen.
Now, line 12 executes. After line 12 is done, the main program exits and control returns to the JVM.
Time complexity of recursive factorial
O(n).
Space complexity of recursive factorial
O(n).
Time complexity of iterative factorial
The iterative version of factorial can be done by the following algorithm:
public static int fact(int n)
{
}
Space complexity of iterative factorial
About recursion
Recursive methods are in general less space efficient then their iterative counterparts, due to their
recursive nature. However, recursion often can be used to solve problems that would be otherwise
difficult to solve iteratively.
When we have developed a recursive method, sometimes it is difficult to convince ourselves as to why
the method works. That is why we should walk through a recursive method to make sure it does what
we expect it to do. However, we can informally verify that a recursive method works by checking the
following 3 questions:
1. Does it have at least 1 base case?
2. Will a base case ever be reached?
3. Assuming the recursive calls all return the proper value, does the method as a whole work?
For a recursive method to work, the answer to the above 3 questions must all be yes.
Consider the following version of method fact:
public static int fact(int n)
{
return n* fact(n-1);
}
Consider the following version of method fact:
public static int fact(int n)
{
if ((n==0)||(n==1)) return 1;
else return n* fact(n+1);
}
Consider the following version of method fact:
public static int fact(int n)
{
if ((n==0)||(n==1)) return 1;
else return fact(n-1);
}
Consider the following version of method fact:
public static int fact(int n)
{
if ((n==0)||(n==1)) return 1;
else return n* fact(n-1);
}
//line 4
A recursive method - power
The Math library as a pow method. Here is its API:
Usage:
public static double pow(double x, double y)
Return:
The value xy
//Example 23-1-5
import java.util.Scanner;
class Demo
{
public static void main(String[] args)
{
Scanner keyboard=new Scanner(System.in);
int x=0, y=0, answer=0;
System.out.println("I will compute x to the power y");
System.out.print("Enter integer x: ");
x=keyboard.nextInt();
System.out.print("Enter integer y: ");
y=keyboard.nextInt();
answer= myPow(x,y);
System.out.println(x+" to the power "+y+" is "+answer);
}
//Pre: y>0
//Return: The value xy
public static int myPow(int x, int y)
{
}
}
//line 5
A recursive method - binSearch
//Example 23-1-6
//Demo.java
import java.util.Scanner;
class Demo
{
public static void main(String[] args)
{
Scanner keyboard=new Scanner(System.in);
int[] arr={3,5,6,8,11};
System.out.print("Enter a key:");
int key=keyboard.nextInt();
int index=binSearch(arr, 0, arr.length-1, key);
if (index == -1) System.out.print("Key not in");
else System.out.print("Key found at index "+index);
}
//Desc: search key in an array of integers in ascending order
//Pre: arr[low]..arr[high] in ascending order
//Return: The index of key in arr[low]..arr[high] if found, -1 if not found
public static int binSearch (int[] arr, int low, int high, int key)
{
}
}
Recursive helper methods
//Example 23-1-7
//Demo.java
import java.util.Scanner;
class Demo
{
public static void main(String[] args)
{
Scanner keyboard=new Scanner(System.in);
int[] arr={3,5,6,8,11};
System.out.print("Enter a key:");
int key=keyboard.nextInt();
int index= binSearch (arr, key);
if (index == -1) System.out.print("Key not in");
else System.out.print("Key found at index "+index);
}
//Desc: search key in an array of integers in ascending order
//Pre: Elements in arr in ascending order
//Return: The index of key in arr if found, -1 if not found
public static int binSearch(int[] arr, int key)
{
return binSearch(arr, 0, arr.length-1, key);
}
//Desc: search key in an array of integers in ascending order
//Pre: arr[low]..arr[high] in ascending order
//Return: The index of key in arr[low]..arr[high] if found, -1 if not found
public static int binSearch(int[] arr, int low, int high, int key)
{
int mid=0;
if (low>high) return -1;
mid= (low+high) /2;
if (key==arr[mid]) return mid;
if (key<arr[mid]) return binSearch (arr, low, mid-1, key);
else return binSearch (arr, mid+1, high, key);
}
}
Selection sort
//Example 23-1-8
//Demo.java
public class Demo
{
public static void main(String[] args)
{
int[] arr={3,5,6,2,4};
selectionSort(arr);
for (int j=0; j<arr.length; ++j)
System.out.print(arr[j]+" ");
}
//Desc: Sorts the elements of an array in ascending order
//Post: Elements of arr sorted in ascending order
public static void selectionSort(int[] arr)
{
}
//Desc: Sorts the elements of an array in ascending order
//Post: All elements in arr starting at arr[low] sorted in ascending order
public static void selectionSort(int[] arr, int low)
{
}
}
Insertion sort
Fibonacci
//Example 23-1-9
//Demo.java
import java.util.Scanner;
class Demo
{
public static void main(String[] args)
{
Scanner keyboard=new Scanner(System.in);
int n=0;
System.out.print("Enter an integer n and I will print the nth term in" +
" the Fibonacci sequence:");
n=keyboard.nextInt();
System.out.println("Iterative: "+iFib(n));
}
//Desc: compute the nth term of Fibonacci sequence 0, 1, 1, 2, 3, 5, 8, ...
//
the 0th term is 0, the 1st term is 1, the 2nd term is 1, and so on.
//Pre: n>=0
//Return: the nth term of fibonacci sequence
public static long iFib (int n)
{
long oneBack=1, twoBack =0, cur=0;
if (n<2) return n;
else
return cur;
}
}
//Example 23-1-10
//Demo.java
import java.util.Scanner;
class Demo
{
public static void main(String[] args)
{
Scanner keyboard=new Scanner(System.in);
int n=0;
System.out.print("Enter an integer n and I will print the nth term in" +
" the Fibonacci sequence:");
n=keyboard.nextInt();
System.out.println("Recursive: "+rFib(n));
}
//Desc: compute the nth term of Fibonacci sequence 0, 1, 1, 2, 3, 5, 8, ...
//
the 0th term is o, the 1st term is 1, the 2nd term is 2, and so on.
//Pre: n>=0
//Return: the nth term of fibonacci sequence
public static long rFib (int n)
{
}
}
Class BigInteger
Method iFib uses the data type long instead of int since the Fibonacci sequence grows very quickly.
However, even with a long, the Fibonacci sequence will overflow a long with the 93 rd term:
The Java API has a class called BigInteger, to support immutable arbitrary-precision integers.
BigInteger provides analogues to all of Java's primitive integer operators. BigInteger is in package
java.math.
--------------------------------------------------------------------------------------------------------------------Usage: public BigInteger(String val)
Pre:
The String val represents a legal integer which consists of an optional minus sign followed by a
sequence of one or more decimal digits. The String may not contain any extraneous characters
(whitespace, for example).
Post:
This BigInteger initialized to contain the decimal integer represented by the String val.
Throw: NumberFormatException if val is not a valid representation of a BigInteger.
Usage: public BigInteger add(BigInteger val)
Return: this+val.
Usage: public BigInteger subtract(BigInteger val)
Return: this-val.
Usage: public BigInteger divide(BigInteger val)
Return: this/val.
Usage: public BigInteger multiply(BigInteger val)
Return: this*val.
Usage: public String toString()
Return: the decimal String representation of this BigInteger.
--------------------------------------------------------------------------------------------------------------------Figure 23-1-1. Some useful method of class BigInteger.
//Example 23-1-11
//Demo.java
import java.math.BigInteger;
class Demo
{
public static void main(String[] args)
{
BigInteger n1=new BigInteger("4660046610375530309");
BigInteger n2=new BigInteger("7540113804746346429");
BigInteger result1=n1.add(n2);
BigInteger result2=n1.subtract(n2);
BigInteger result3=n1.divide(n2);
BigInteger result4=n1.multiply(n2);
System.out.println(result1);
System.out.println(result2);
System.out.println(result3);
System.out.println(result4);
}
}
//Example 23-1-12
//Demo.java
import java.util.Scanner;
import java.math.BigInteger;
class Demo
{
public static void main(String[] args)
{
Scanner keyboard=new Scanner(System.in);
int n=0;
System.out.print("Enter an integer n and I will print the nth term in" +
" the Fibonacci sequence:");
n=keyboard.nextInt();
System.out.println("Iterative: "+iFib(n));
}
//Desc: compute the nth term of Fibonacci sequence 0, 1, 1, 2, 3, 5, 8, ...
//
the 0th term is 0, the 1st term is 1, the 2nd term is 1, and so on.
//Pre: n>=0
//Return: the nth term of fibonacci sequence
public static BigInteger iFib (int n)
{
}
}
Tower of Hanoi
//Example 23-1-13
import java.util.Scanner;
class Demo
{
//Solve the tower of Hanoi puzzle. User enters the number of disks. The program displays a list
//of instructions in the following form:
//
A to B
//
B to C
//
…
//where A is the initial peg, C is the destination peg, and B is the auxiliary peg.
public static void main(String[] args)
{
Scanner keyboard=new Scanner(System.in);
int n=0;
//line 13
System.out.print("Enter number of disks: "); //line 14
n=keyboard.nextInt();
//line 15
hanoi (n, 'A', 'B', 'C');
//line 16
}
//Desc: Solve the tower of Hanoi puzzle
//Pre: n is the # of disks in tower, and it is >=1
//
from is the peg where the disks are originally on
//
aux is the auxiliary peg where the disks can be on temporarily
//
to is the peg where the disks are to be moved to
//Output:A sequence of instructions when followed, moves the n disks from "from" to "to"
public static void hanoi(int n, char from, char aux, char to)
{
}
}
Convert a base 10 number to base b
//Example 23-1-14
import java.util.Scanner;
class Demo
{
//Convert a base 10 number to base 2, 8, 16
public static void main(String[] args)
{
Scanner keyboard=new Scanner(System.in);
System.out.print("Enter a base 10 number: ");
int n=keyboard.nextInt();
System.out.println("Binary: "+ convert(n, 2));
System.out.println("Octal: "+ convert(n, 8));
System.out.println("Hex: "+ convert(n, 16));
}
//Desc: convert n to base b
//Pre: n>0, 16>=b>1
//Post: the string that represents n in base b
public static String convert (int n, int b)
{
}
}
Greatest common divisor (GCD, or GCF)
Assume a and b are 2 positive integers. The greatest common divisor of a and b, gcd(a,b), is the
largest integer that divides both a and b. As a special case, when one of them is 0, say b, gcd(a,0)=a.
//Example 23-1-16
Find gcd(18, 12)
//Example 23-1-17
Find gcd(32, 9)
//Example 23-1-18
//Demo.java
import java.util.Scanner;
class Demo
{
public static void main(String[] args)
{
Scanner f=new Scanner(System.in);
System.out.print("Enter 2 integers, separated by white space: ");
int n1=f.nextInt();
int n2=f.nextInt();
System.out.println("GCD of "+n1+" and "+n2+" is "+gcd(n1, n2));
}
public static int gcd(int m, int n)
{
}
}
Euclidean algorithm
The Greek mathematician Euclid provided an elegant recursive algorithm for computing gcd(a,b). The
algorithm relies on the fact that gcd(a,b)=gcd(b, a%b), and gcd(a,0)=a.
//Example 23-1-19
//Demo.java
import java.util.Scanner;
class Demo
{
public static void main(String[] args)
{
Scanner f=new Scanner(System.in);
System.out.print("Enter 2 inetgers, separated by white space: ");
int n1=f.nextInt();
int n2=f.nextInt();
System.out.println("GCD of "+n1+" and "+n2+" is "+gcd(n1, n2));
}
//Pre: m and n are nonnegative integers
//Return: the gcd of m and n
public static int gcd(int m, int n)
{
}
}
Programming Exercises
1.
Implement isPalindrome as a recursive method. Write a driver to test it.
Usage:
public static boolean isPalindrome (String s, int start, int last)
Desc:
Determines if a string is a palindrome. A palindrome is a string (for this assignment,
consists of lowercase letters only) that reads the same forward and backward
Pre:
s consists of lowercase letters only.
Return:
true if the string s in index [start, last) is a palindrome.
Hint: What are the base cases?
Hand in/email:
Palindrome.java (class Palindrome has at least 2 methods, main and isPalindrome)
2.
Implement insertionSort as a recursive method. Write a driver to test it.
Hint:
You must understand the iterative insertion sort first.
//Desc: Sorts an array of integers in ascending order
public static void insertionSort(int[] arr)
{
int pulled=0, i=0;
for (int pass=1; pass<arr.length; ++pass)
{
pulled=arr[pass];
i=pass-1;
while (i>=0)
{
if (pulled<arr[i])
{
arr[i+1]=arr[i];
i=i-1;
}
else break;
}
arr[i+1]=pulled;
}
}
Let insertionSort(arr, high) mean to sort arr[0]..arr[high]. The recursive definition for
insertionSort(arr, high) is:
o Base case when high=0 (the array has 1 elements):
insertionSort(arr, high) sorts nothing and returns
o Recursive case when high>0
insertionSort(arr, high) = insertionSort(arr, high-1)
insert arr[high] into the sorted arr[0]..arr[high-1]
Hand in/email:
Sort.java (class Sort has at least 2 methods, main and insertionSort)
3.
Implement intToEnglish as a recursive method. Write a driver to test it.
Usage:
public static String intToEnglish (int n)
Desc:
Converts a positive integer to English.
Pre:
n must be positive.
Return:
A string consists of English words of n. For example, if n=372, intToEnglish returns
the String "three seven two".
Hint: Easy. Similar to convert in Example 23-1-14.
Hand in/email:
IntToEng.java (class IntToEng has at least 2 methods, main and intToEnglish)
© Copyright 2026 Paperzz