Professional Documents
Culture Documents
05 Simple Recursion
05 Simple Recursion
21-Apr-19
A short list of categories
Algorithm types we will consider include:
Simple recursive algorithms
Backtracking algorithms
Divide and conquer algorithms
Dynamic programming algorithms
Greedy algorithms
Branch and bound algorithms
Brute force algorithms
Randomized algorithms
2
Simple recursive algorithms
A simple recursive algorithm:
Solves the base cases directly
Recurs with a simpler subproblem or subproblems
May do some extra work to convert the solution to the
simpler subproblem into a solution to the given problem
I call these “simple” because several of the other
algorithm types are inherently recursive
3
Some example algorithms
We will look briefly at the following simple
recursive algorithms:
Factorial
All permutations
Tree traversal
Flood fill
Quicksort
Towers of Hanoi
Ackermann’s function
We will also consider the equivalence of loops and
recursion
4
Factorial
The factorial function is the “Hello World” of recursion
public static long factorial(int n) {
if (n <= 1) return 1;
else return n * factorial(n - 1);
}
The problem with this example is that it can be done almost
as easily with a loop (so why bother learning recursion?)
public static long factorial(int n) {
int result = 1;
while (n > 1) {
result *= n;
n--;
}
}
In the following slides, we look at some example problems
for which recursion really is simpler
5
Permutations
A permutation of a set of objects is a particular ordering
of those objects
When we speak of “all permutations” of a set, we mean
all possible ways of ordering those objects
Examples:
Given the empty set { }, the only possible permutation is { }
Given the set {A}, the only possible permutation is {A}
Given the set {A, B}, the possible permutations are {AB, BA}
Given the set {A, B, C}, the possible permutations are
{ABC, ACB, BAC, BCA, CAB, CBA}
Etc.
6
Finding all permutations of n objects
To find all permutations of n objects:
Find all permutations of n-1 of those objects
Insert the remaining object into all possible positions of each
permutation of n-1 objects
Example: To find all permutations of 3 objects {A, B, C}
Find all permutations of 2 of the objects, say B and C:
B C and C B
Insert the remaining object, A, into all possible positions
(marked by ^) in each of the permutations of B and C:
^ B ^ C ^ and ^ C ^ B ^
ABC BAC BCA ACB CAB CBA
7
A program to find permutations
We will develop complete Java code to find all permutations of a
nonempty String of characters
ArrayList<String> permutationsOf(String s) {
if (s.length() == 1) {
// return a new ArrayList containing just s
}
else {
// separate the first character from the rest
// get all permutationsOf ( the rest of the characters )
// for each permutation,
// add the first character in all possible positions, and
// put each result into a new ArrayList
}
// return the new ArrayList
}
8
permutationsOf(String), part I
ArrayList<String> permutationsOf(String s) {
ArrayList<String> result = new ArrayList<String>();
9
permutationsOf(String), part II
else {
// separate the first character from the rest
char first = s.charAt(0);
String rest = s.substring(1);
11
Trees
internal
A tree is composed of nodes nodes root
Each node contains a value
A
Each node may have children
One special node is called the B C D E
root of the tree
Nodes with children are F G H I J K
called internal nodes
Nodes without children are L M N
called leaves
leaves
12
Tree traversals
It’s easy to traverse (“walk”) a tree recursively
Here’s a recursive method which, if called with the root of a
tree, will print out all the values in the tree:
void printTree(Node n) {
print the value in node n;
for each child c of n, printTree(c)
Or, in actual Java:
void printTree(Node n) {
System.out.println(n.getValue());
Iterator iter = node.getChildren().iterator();
while (iter.hasNext()) {
printTree(iter.next());
}
}
Many data structures are best handled recursively
13
Flood fill
To “flood fill” an area with color oldColor, starting from
a particular pixel of color newColor:
14
Quicksort
Quicksort is (in some sense) the fastest sorting
algorithm known
From an array of numbers, pick one to use as a pivot
45 3 17 48 72 25 8 99 14 9 12 21 81 64 33 12
(we’ll use 45 as the pivot)
Partition the numbers into those smaller than or equal to the
pivot, and those larger than the pivot
3 17 25 8 14 9 12 21 33 12 45 48 72 99 81 64
15
Towers of Hanoi
“Towers of Hanoi” is commonly used as an example
of a problem with a simple recursive solution
There are three pegs
The first (source) peg holds some
number of rings
You want to move all rings to the
last (destination) peg
You can only move one ring at a
time
You can never put a larger ring on src aux dest
top of a smaller ring
You have one auxiliary peg you
can use
16
Solution to Towers of Hanoi
17
Ackermann’s function
Ackermann’s function is a deceptively simple little set
of equations:
A(0, y) = y + 1
A(x, 0) = A(x - 1, 1)
A(x, y) = A(x - 1, A(x, y - 1))
This can easily be written as a recursive method
A(4, 0) = 13
A(4, 1) = 65533
A(4, 2) = 2 65536-3
After this, the numbers start to get huge quickly
This function takes a long time to compute (why?)
18
Recursion isn’t necessary
Computers don’t have “recursive hardware”!
When a higher-level language is compiled, recursive calls are
implemented with a stack
When you enter a method, all its local variables (including its formal
parameters) are created and put onto a stack
The method operates on its own copies of the local variables
When the method exits, its local variables are removed from the stack
The compiler changes recursive code into nonrecursive code
It follows, then, that anything you could do recursively, you
could also do nonrecursively by using loops and a stack
19
Tree traversals, again
Here’s a recursive method which, if called with the root of a
tree, will print out all the values in the tree:
void printTree(Node n) {
print the value in node n;
for each child c of n {
printTree(c)
}
}
Here it is without recursion:
void printTree(Node n) {
create a new stack s;
push n onto s;
while (s is not empty) {
remove a node m from s and print it
push the children of m (if any) onto stack s
}
}
20
Loops aren’t necessary
You can replace any recursion with loops (and a stack for local variables)
In addition, you can replace any loop with a recursion
For example, consider zeroing out an array:
static void zeroOut(int[] a) {
for (int i = 0; i < a.length; i++) a[i] = 0;
}
Or:
static void zeroOut(int[] a, int n) { // call with n = 0
if (n < a.length) {
a[n] = 0;
zeroOut(a, n + 1);
}
}
Of course, this is an example, not a proof
It can be proved (we won’t do it here) that loops can always be replaced by
recursions
21
Closing comments
The intent of this set of slides is to get you more
familiar with some of the uses of recursion
Recursion and loops are, in some sense, equivalent--
anything you can do with one, you can do with the
other
Once you understand recursion, though, it is often
simpler to use recursion than to use loops
22
The End
23