CH 12
CH 12
Chapter 12
Recursion
Recursion
• recursion: The definition of an operation in terms of itself.
– Solving a problem using recursion depends on solving
smaller occurrences of the same problem.
2
Why learn recursion?
• "cultural experience" - A different way of thinking of problems
3
Exercise
• (To a student in the front row)
How many students total are directly behind you in your
"column" of the classroom?
5
Recursive algorithm
• Number of people behind me:
– If there is someone behind me,
ask him/her how many people are behind him/her.
• When they respond with a value N, then I will answer N + 1.
6
Recursion and cases
• Every recursive algorithm involves at least 2 cases:
– base case: A simple occurrence that can be answered directly.
7
Another recursive task
• How can we remove exactly half of the M&M's in a large bowl,
without dumping them all out or being able to count them?
– What if multiple people help out with solving the problem?
Can each person do a small part of the work?
8
Recursion in Java
• Consider the following method to print a line of * characters:
10
Handling more cases
• Handling additional cases, with no loops (in a bad way):
public static void printStars(int n) {
if (n == 1) {
// base case; just print one star
System.out.println("*");
} else if (n == 2) {
System.out.print("*");
System.out.println("*");
} else if (n == 3) {
System.out.print("*");
System.out.print("*");
System.out.println("*");
} else if (n == 4) {
System.out.print("*");
System.out.print("*");
System.out.print("*");
System.out.println("*");
} else ...
} 11
Handling more cases 2
• Taking advantage of the repeated pattern (somewhat better):
public static void printStars(int n) {
if (n == 1) {
// base case; just print one star
System.out.println("*");
} else if (n == 2) {
System.out.print("*");
printStars(1); // prints "*"
} else if (n == 3) {
System.out.print("*");
printStars(2); // prints "**"
} else if (n == 4) {
System.out.print("*");
printStars(3); // prints "***"
} else ...
}
12
Using recursion properly
• Condensing the recursive cases into a single case:
public static void printStars(int n) {
if (n == 1) {
// base case; just print one star
System.out.println("*");
} else {
// recursive case; print one more star
System.out.print("*");
printStars(n - 1);
}
}
13
"Recursion Zen"
• The real, even simpler, base case is an n of 0, not 1:
public static void printStars(int n) {
if (n == 0) {
// base case; just end the line of output
System.out.println();
} else {
// recursive case; print one more star
System.out.print("*");
printStars(n - 1);
}
}
15
A recursive trace
mystery(648):
int a = 648 / 10; // 64
int b = 648 % 10; // 8
return mystery(a + b); // mystery(72)
mystery(72):
int a = 72 / 10; // 7
int b = 72 % 10; // 2
return mystery(a + b); // mystery(9)
mystery(9):
return 9;
16
Recursive tracing 2
• Consider the following recursive method:
public static int mystery(int n) {
if (n < 10) {
return (10 * n) + n;
} else {
int a = mystery(n / 10);
int b = mystery(n % 10);
return (100 * a) + b;
}
}
17
A recursive trace 2
mystery(348)
int a = mystery(34);
• int a = mystery(3);
return (10 * 3) + 3; // 33
• int b = mystery(4);
return (10 * 4) + 4; // 44
• return (100 * 33) + 44; // 3344
int b = mystery(8);
return (10 * 8) + 8; // 88
19
pow solution
// Returns base ^ exponent.
// Precondition: exponent >= 0
public static int pow(int base, int exponent) {
if (exponent == 0) {
// base case; any number to 0th power is 1
return 1;
} else {
// recursive case: x^y = x * x^(y-1)
return base * pow(base, exponent - 1);
}
}
20
An optimization
• Notice the following mathematical property:
312 = 531441 = 96
= (32)6
531441 = (92)3
= ((32)2)3
22
Exercise
• Write a recursive method printBinary that accepts an
integer and prints that number's representation in binary (base
2).
– Case analysis:
• What is/are easy numbers to print in binary?
• Can we express a larger number in terms of a smaller number(s)?
25
printBinary solution 2
// Prints the given integer's binary representation.
public static void printBinary(int n) {
if (n < 0) {
// recursive case for negative numbers
System.out.print("-");
printBinary(-n);
} else if (n < 2) {
// base case; same as base 10
System.out.println(n);
} else {
// recursive case; break number apart
printBinary(n / 2);
printBinary(n % 2);
}
}
26
Exercise
• Write a recursive method isPalindrome accepts a String
and returns true if it reads the same forwards as backwards.
– isPalindrome("madam") true
– isPalindrome("racecar") true
– isPalindrome("step on no pets") true
– isPalindrome("able was I ere I saw elba") true
– isPalindrome("Java") false
– isPalindrome("rotater") false
– isPalindrome("byebye") false
– isPalindrome("notion") false
27
Exercise solution
// Returns true if the given string reads the same
// forwards as backwards.
// Trivially true for empty or 1-letter strings.
public static boolean isPalindrome(String s) {
if (s.length() < 2) {
return true; // base case
} else {
char first = s.charAt(0);
char last = s.charAt(s.length() - 1);
if (first != last) {
return false;
} // recursive case
String middle = s.substring(1, s.length() - 1);
return isPalindrome(middle);
}
}
28
Exercise solution 2
// Returns true if the given string reads the same
// forwards as backwards.
// Trivially true for empty or 1-letter strings.
public static boolean isPalindrome(String s) {
if (s.length() < 2) {
return true; // base case
} else {
return s.charAt(0) == s.charAt(s.length() - 1)
&& isPalindrome(s.substring(1, s.length() - 1));
}
}
29
Exercise
• Write a recursive method reverseLines that accepts a file
Scanner and prints the lines of the file in reverse order.
– Example input file: Expected console output:
Roses are red, Are belong to you.
Violets are blue. All my base
All my base Violets are blue.
Are belong to you. Roses are red,
30
Reversal pseudocode
• Reversing the lines of a file:
– Read a line L from the file.
– Print the rest of the lines in reverse order.
– Print the line L.
• If only we had a way to reverse the rest of the lines of the file....
31
Reversal solution
public static void reverseLines(Scanner input) {
if (input.hasNextLine()) {
// recursive case
String line = input.nextLine();
reverseLines(input);
System.out.println(line);
}
}
32
Tracing our algorithm
• call stack: The method invocations running at any one time.
reverseLines(new Scanner("poem.txt"));
public static void reverseLines(Scanner input) {
if (input.hasNextLine()) {
String line = input.nextLine(); // "Roses are red,"
public static void reverseLines(Scanner input) {
reverseLines(input);
if (input.hasNextLine()) {
System.out.println(line);
String line = input.nextLine(); // "Violets are blue."
} static
public void reverseLines(Scanner input) {
reverseLines(input);
} if (input.hasNextLine()) {
System.out.println(line);
String line = input.nextLine(); // "All my base"
} static
public void reverseLines(Scanner input) {
reverseLines(input);
} if (input.hasNextLine()) {
System.out.println(line);
String line = input.nextLine(); // "Are belong to you."
} static
public void reverseLines(Scanner input) {
reverseLines(input);
} if (input.hasNextLine()) { // false
System.out.println(line);
} ...
} file:
} input output:
}
Roses are red, Are belong to you.
Violets are blue. All my base
All my base Violets are blue.
Are belong to you. Roses are red, 33
Exercise
• Write a method crawl accepts a File parameter and prints
information about that file.
– If the File object represents a normal file, just print its name.
– If the File object represents a directory, print its name and
information about every file/directory inside it, indented.
cse143
handouts
syllabus.doc
lecture_schedule.xls
homework
1-sortedintlist
ArrayIntList.java
SortedIntList.java
index.html
style.css
Constructor/method Description
File(String) creates File object representing file with given name
canRead() returns whether file is able to be read
delete() removes file from disk
exists() whether this file exists on disk
getName() returns file's name
isDirectory() returns whether this object represents a directory
length() returns number of bytes in file
listFiles() returns a File[] representing files in this directory
renameTo(File) changes name of file
35
Public/private pairs
• We cannot vary the indentation without an extra parameter:
public static void crawl(File f, String indent) {
36
Exercise solution 2
// Prints information about this file,
// and (if it is a directory) any files inside it.
public static void crawl(File f) {
crawl(f, ""); // call private recursive helper
}
37
Recursive Backtracking
Exercise: Permutations
• Write a method permute that accepts a string as a parameter
and outputs all possible rearrangements of the letters in that
string. The arrangements may be output in any order.
– Example: MARTY
MARYT
MYRAT
MYRTA
ATYMR
ATYRM
RTMAY
RTMYA
TARMY
TARYM
YMTAR
YMTRA
permute("MARTY") MATRY MYTAR AYMRT RTAMY TAYMR YAMRT
MATYR MYTRA AYMTR RTAYM TAYRM YAMTR
outputs the following MAYRT AMRTY AYRMT RTYMA TRMAY YARMT
MAYTR AMRYT AYRTM RTYAM TRMYA YARTM
sequence of lines: MRATY AMTRY AYTMR RYMAT TRAMY YATMR
MRAYT AMTYR AYTRM RYMTA TRAYM YATRM
MRTAY AMYRT RMATY RYAMT TRYMA YRMAT
MRTYA AMYTR RMAYT RYATM TRYAM YRMTA
MRYAT ARMTY RMTAY RYTMA TYMAR YRAMT
MRYTA ARMYT RMTYA RYTAM TYMRA YRATM
MTARY ARTMY RMYAT TMARY TYAMR YRTMA
MTAYR ARTYM RMYTA TMAYR TYARM YRTAM
MTRAY ARYMT RAMTY TMRAY TYRMA YTMAR
MTRYA ARYTM RAMYT TMRYA TYRAM YTMRA
MTYAR ATMRY RATMY TMYAR YMART YTAMR
MTYRA ATMYR RATYM TMYRA YMATR YTARM
MYART ATRMY RAYMT TAMRY YMRAT YTRMA
MYATR ATRYM RAYTM TAMYR YMRTA YTRAM
39
Examining the problem
• Think of each permutation as a set of choices or decisions:
– Which character do I want to place first?
– Which character do I want to place second?
– ...
M ARTY A MRTY
...
MA RTY MR ATY MT ARY MY ART
... ...
MAR TY MAT RY MAY RT ...
MYA RT
... ...
MART Y MARY T MAYR T MAYT R
41
Backtracking
• backtracking: A general algorithm for finding solution(s) to a
computational problem by trying partial solutions and then
abandoning them ("backtracking") if they are not suitable.
– a "brute force" algorithmic technique (tries all paths; not clever)
– often (but not always) implemented recursively
Applications:
– producing all permutations of a set of values
– parsing languages
– games: anagrams, crosswords, word jumbles, 8 queens
– combinatorics and logic programming
42
Backtracking algorithms
A general pseudo-code algorithm for backtracking problems:
explore(choices):
– if there are no more choices to make: stop.
– else:
• Make a single choice C from the set of choices.
– Remove C from the set of choices.
• Un-make choice C.
– Backtrack!
43
Backtracking strategies
• When solving a backtracking problem, ask these questions:
– What are the "choices" in this problem?
• What is the "base case"? (How do I know when I'm out of choices?)
52
Exercise client code
import java.util.*; // for ArrayList
public class SolveDominoes {
public static void main(String[] args) {
// [(1|4), (2|6), (4|5), (1|5), (3|5)]
List<Domino> dominoes = new ArrayList<Domino>();
dominoes.add(new Domino(1, 4));
dominoes.add(new Domino(2, 6));
dominoes.add(new Domino(4, 5));
dominoes.add(new Domino(1, 5));
dominoes.add(new Domino(3, 5));
System.out.println(hasChain(dominoes, 5, 5)); // true
System.out.println(hasChain(dominoes, 1, 5)); // true
System.out.println(hasChain(dominoes, 1, 3)); // true
System.out.println(hasChain(dominoes, 1, 6)); // false
System.out.println(hasChain(dominoes, 1, 2)); // false
}
public static boolean hasChain(List<Domino> dominoes,
int start, int end) {
...
}
}
53
Exercise solution
public static boolean hasChain(List<Domino> dominoes,
int start, int end) {
if (start == end) {
return true; // base case
} else {
for (int i = 0; i < dominoes.size(); i++) {
Domino d = dominoes.remove(i); // choose
if (d.first() == start) { // explore
if (hasChain(dominoes, d.second(), end)) {
return true;
}
} else if (d.second() == start) {
if (hasChain(dominoes, d.first(), end)) {
return true;
}
}
dominoes.add(i, d); // un-choose
}
return false;
}
}
54
Exercise: Print chain
• Write a variation of your hasChain method that also prints
the chain of dominoes that it finds, if any.
hasChain(dominoes, 1, 3);
55
Exercise solution
public static boolean hasChain(List<Domino> dominoes, int start, int end) {
Stack<Domino> chosen = new Stack<Domino>();
return hasChain(dominoes, chosen, start, end);
}
private static boolean hasChain(List<Domino> dominoes,
Stack<Domino> chosen, int start, int end) {
if (start == end) {
System.out.println(chosen);
return true; // base case
} else {
for (int i = 0; i < dominoes.size(); i++) {
Domino d = dominoes.remove(i); // choose
if (d.first() == start) { // explore
chosen.push(d);
if (hasChain(dominoes, chosen, d.second(), end)) {
return true;
}
chosen.pop();
} else if (d.second() == start) {
d.flip();
chosen.push(d);
if (hasChain(dominoes, chosen, d.second(), end)) {
return true;
}
chosen.pop();
}
dominoes.add(i, d); // un-choose
}
return false;
}
}
56
The "8 Queens" problem
• Consider the problem of trying to place 8 queens on a chess
board such that no queen can attack another queen.
Q
– What are the "choices"?
Q
– How do we "make" or Q
"un-make" a choice?
Q
Q
57
Naive algorithm
• for (each square on board):
– Place a queen there. 1 2 3 4 5 6 7 8
– Try to place the rest 1 Q ... ... ... ... ... ... ...
of the queens.
2 ... ... ... ... ... ... ... ...
– Un-place the queen.
3 ...
4
– How large is the
solution space for 5
this algorithm? 6
• 64 * 63 * 62 * ...
7
8
58
Better algorithm idea
• Observation: In a working
solution, exactly 1 queen 1 2 3 4 5 6 7 8
must appear in each 1 Q ... ...
row and in
each column. 2 ... ...
3 Q ...
– Redefine a "choice"
4 ...
to be valid placement
of a queen in a 5 Q
particular column.
6
Method/Constructor Description
public Board(int size) construct empty board
public boolean isSafe(int row, int true if queen can be
column) safely placed here
public void place(int row, int column) place queen here
public void remove(int row, int column) remove queen from
here
public String toString() text display of board
• Write a method solveQueens that accepts a Board as a
parameter and tries to place 8 queens on it safely.
– Your method should stop exploring if it finds a solution.
60
Exercise solution
// Searches for a solution to the 8 queens problem
// with this board, reporting the first result found.
public static void solveQueens(Board board) {
if (!explore(board, 1)) {
System.out.println("No solution found.");
} else {
System.out.println("One solution is as follows:");
System.out.println(board);
}
}
...
61
Exercise solution, cont'd.
// Recursively searches for a solution to 8 queens on this
// board, starting with the given column, returning true if a
// solution is found and storing that solution in the board.
// PRE: queens have been safely placed in columns 1 to (col-1)
public static boolean explore(Board board, int col) {
if (col > board.size()) {
return true; // base case: all columns are placed
} else {
// recursive case: place a queen in this column
for (int row = 1; row <= board.size(); row++) {
if (board.isSafe(row, col)) {
board.place(row, col); // choose
if (explore(board, col + 1)) { // explore
return true; // solution found
}
b.remove(row, col); // un-choose
}
}
return false; // no solution found
}
}
62