Recursion
Recursion
Recursive Backtracking
Iteration
When we encounter a problem that requires repetition,
we often use iteration i.e., some type of loop.
Sample problem: printing the series of integers from
n1 to n2, where n1 <= n2.
example: printSeries(5, 10) should print the following:
5, 6, 7, 8, 9, 10
Recursion
An alternative approach to problems that require repetition
is to solve them using recursion.
A recursive method is a method that calls itself.
Applying this approach to the print-series problem gives:
public static void printSeries(int n1, int n2) {
if (n1 == n2) {
System.out.println(n2);
} else {
System.out.print(n1 + ", ");
printSeries(n1 + 1, n2);
}
}
Recursive Problem-Solving
When we use recursion, we solve a problem by reducing it
to a simpler problem of the same kind.
We keep doing this until we reach a problem that is
simple enough to be solved directly.
This simplest problem is known as the base case.
public static void printSeries(int n1, int n2) {
if (n1 == n2) {
// base case
System.out.println(n2);
} else {
System.out.print(n1 + ", ");
printSeries(n1 + 1, n2);
}
}
// recursive case
System.out.println(i);
mystery(i 1);
System.out.println(i);
}
Example: sum(3)
n
total
return 0
n
total
n
total
n
total
1
1
total = 1 + sum(0)
=1+ 0
return 1
n
total
n
total
n
total
n
total
n
total
n
total
n
total
n
total
n
total
n
total
2
3
n
total
return 3
n
total
time
Infinite Recursion
We have to ensure that a recursive method will eventually
reach a base case, regardless of the initial input.
Otherwise, we can get infinite recursion.
produces stack overflow there's no room for
more frames on the stack!
Example: here's a version of our sum() method that uses
a different test for the base case:
public static int sum(int n) {
if (n == 0) {
return 0;
}
int total = n + sum(n 1);
return total;
}
3
6
Thinking Recursively
When solving a problem using recursion, ask yourself these
questions:
1. How can I break this problem down into one or more
smaller subproblems?
make recursive method calls to solve the subproblems
See ~cscie119/examples/recursion/Power.java
Example: power1(5,3)
x5 n3
x5 n1
x5 n1
x5 n1
return 5*1
x5 n2
x5 n2
x5 n2
x5 n2
x5 n3
x5 n3
x5 n3
x5 n3
x5 n2
return 5*5
x5 n3
x5 n3
return 5*25
time
Analyzing power2
How many method calls would it take to compute 21000 ?
power2(2, 1000)
power2(2, 500)
power2(2, 250)
power2(2, 125)
power2(2, 62)
power2(2, 31)
power2(2, 15)
power2(2, 7)
power2(2, 3)
power2(2, 1)
power2(2, 0)
Thinking recursively:
Common Mistake
This version of the method does not work:
public static int numOccur(char ch, String str) {
if (str == null || str.equals("")) {
return 0;
}
int count = 0;
if (str.charAt(0) == ch) {
count++;
}
numOccur(ch, str.substring(1));
return count;
}
example:
removeVowels("recurse")
should return
"rcrs"
Thinking recursively:
Q
Q
Q
Q
Q
Q
Q
Q
col 0: safe
row 1:
Q
Q
col 2: safe
Q
Q
Q
Q
Q
Q
Q
Q
col 1: safe
row 3:
Q
Q
Q
Q
Q
Q
Q
Q
col 0: same col/diag col 1: same col/diag col 2: same diag col 3: same col/diag
Backtrack to row 2:
Q
Q
Q
row 1:
row 2:
Q
Q
Q
row 3:
Q
Q
Q
Q
Q
Q
Q
Q
A solution!
findSafeColumn() Method
public void findSafeColumn(int row) {
if (row == boardSize) { // base case: a solution!
solutionsFound++;
displayBoard();
if (solutionsFound >= solutionTarget)
System.exit(0);
return;
}
for (int col = 0; col < boardSize; col++) {
if (isSafe(row, col)) {
placeQueen(row, col);
// Move onto the next row.
findSafeColumn(row + 1);
(see ~cscie119/examples/recursion/Queens.java)
Tracing findSafeColumn()
public void findSafeColumn(int row) {
if (row == boardSize) {
// code to process a solution goes here...
}
for (int col = 0; col < BOARD_SIZE; col++) {
if (isSafe(row, col)) {
placeQueen(row, col);
findSafeColumn(row + 1);
removeQueen(row, col);
}
}
}
We can pick up
backtrack!
row: 2
col: 0,1,2,3
row: 0
col: 0
backtrack!
row: 3
col: 0,1,2,3
row: 2
col: 0,1
row: 2
col: 0,1
row: 1
col: 0,1,2
row: 1
col: 0,1,2
row: 1
col: 0,1,2
row: 1
col: 0,1,2,3
row: 1
col: 0,1,2,3
row: 1
col: 0,1,2,3
row: 0
col: 0
row: 0
col: 0
row: 0
col: 0
row: 0
col: 0
row: 0
col: 0
row: 0
col: 0
time
return true;
removeValue(val, n);
}
}
return false;
}
found a solution
val
isValid(val, n)
applyValue(val, n)
removeValue(val, n)
map coloring
variables = each states color
constraints = no two bordering states with the same color