You are on page 1of 23

Simple Recursive Algorithms

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>();

if (s.length() == 1) { // base case


// return a new ArrayList containing just s
result.add(s);
return result;
}
// continued...

9
permutationsOf(String), part II
else {
// separate the first character from the rest
char first = s.charAt(0);
String rest = s.substring(1);

// get all permutationsOf the rest of the characters


ArrayList<String> simpler = permutationsOf(rest); // recursive step

// for each permutation,


for (String permutation : simpler) { // extra work
// add the first character in all possible positions, and
ArrayList additions = insertAtAllPositions(first, permutation);
// put each result into a new ArrayList
result.addAll(additions);
}
return result;
}
}
10
Insert in all positions
 Given one String representing one permutation of n-1 characters,
we want to return all permutations resulting from inserting a
given character in all n possible positions

private ArrayList<String> insertAtAllPositions(char ch, String s) {


ArrayList<String> result = new ArrayList<String>();
for (int i = 0; i <= s.length(); i++) {
String inserted = s.substring(0, i) + ch + s.substring(i);
result.add(inserted);
}
return result;
}

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:

 void floodFill(Pixel p, Color oldColor, Color newColor) {


if p is not oldColor, just return
else {
set color of p to newColor
for each pixel q adjacent to p (horizontally or vertically),
floodFill(q, oldColor, 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

Now quicksort the left side: And the right side:


3 8 9 12 12 14 17 21 25 33 45 48 64 72 81 99

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

src aux dest

 Move the top n-1 rings from src to aux (recursively)


 Move the largest ring from src to dest
 Move the n-1 rings from aux to dest (recursively)

 Notice that there are two recursive calls in this algorithm


 Hence, the number of calls doubles at each level of the recursion
 For a large number of rings, this is a very expensive algorithm!

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

You might also like