0% found this document useful (0 votes)
188 views66 pages

Data Structures AND Algorithms: Lecture Notes 9

The document discusses recursion and recursive algorithms. It begins with an overview of recursive thinking and how recursion can be used to solve problems by breaking them down into smaller subproblems. It provides examples of recursively defining mathematical concepts like factorials and Fibonacci numbers. The document then discusses writing recursive functions and tracing their execution. It provides examples of using recursion to search arrays, including linear and binary search algorithms. Overall the document introduces recursion and illustrates how it can be applied to problem solving.

Uploaded by

elemaniaq
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
188 views66 pages

Data Structures AND Algorithms: Lecture Notes 9

The document discusses recursion and recursive algorithms. It begins with an overview of recursive thinking and how recursion can be used to solve problems by breaking them down into smaller subproblems. It provides examples of recursively defining mathematical concepts like factorials and Fibonacci numbers. The document then discusses writing recursive functions and tracing their execution. It provides examples of using recursion to search arrays, including linear and binary search algorithms. Overall the document introduces recursion and illustrates how it can be applied to problem solving.

Uploaded by

elemaniaq
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 66

Bilgisayar Mühendisliği Bölümü

DATA STRUCTURES
AND
ALGORITHMS

Lecture Notes 9
Recursion

Spring 2008

GIT – Computer Engineering Department


Chapter Outline
 Thinking recursively
 Tracing execution of a recursive method
 Writing recursive algorithms
– Methods for searching arrays
 Solving the Towers of Hanoi problem with recursion
 Processing 2-D images with recursion
 Backtracking to solve search problems, as in mazes
 Recursion vs. Iteration

GIT – Computer Engineering Department Simulation - Lecture Notes 8 2


Recursive Thinking
 Recursion is:
– a problem-solving approach, that can ...
– generate simple solutions to ...
– certain kinds of problems that ...
– would be difficult to solve in other ways
 Recursion splits a problem:
– Into one or more simpler versions of itself

GIT – Computer Engineering Department Simulation - Lecture Notes 8 3


Recursive Thinking: An Example
Strategy for processing nested dolls:
1. if there is only one doll
2. do what it needed for it
else
3. do what is needed for the outer doll
4. Process the inner nest in the same way

GIT – Computer Engineering Department Simulation - Lecture Notes 8 4


Recursive Thinking: Another Example
Strategy for searching a sorted array:
1. if the array is empty
2. return -1 as the search result (not present)
3. else if the middle element == target
4. return subscript of the middle element
5. else if target < middle element
6. recursively search elements before middle
7. else
8. recursively search elements after the middle

GIT – Computer Engineering Department Simulation - Lecture Notes 8 5


Recursive Thinking: The General Approach

1. if problem is “small enough”


2. solve it directly
3. else
4. break into one or more smaller subproblems
5. solve each subproblem recursively
6. combine results into solution to whole problem

GIT – Computer Engineering Department Simulation - Lecture Notes 8 6


Requirements for Recursive Solution
 At least one “small” case that you can solve
directly
 A way of breaking a larger problem down into:
– One or more smaller subproblems
– Each of the same kind as the original
 A way of combining subproblem results into
an overall solution to the larger problem

GIT – Computer Engineering Department Simulation - Lecture Notes 8 7


Tracing a Recursive Method
Overall
result
length(“ace”)

3
return 1 + length(“ce”)

2
return 1 + length(“e”)

1
return 1 + length(“”)
0

GIT – Computer Engineering Department Simulation - Lecture Notes 8 8


Tracing a Recursive Method (2)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 9


Recursive Definitions of Mathematical Formulas

 Mathematicians often use recursive definitions


 These lead very naturally to recursive algorithms
 Examples include:
– Factorial
– Powers
– Greatest common divisor

GIT – Computer Engineering Department Simulation - Lecture Notes 8 10


Recursive Definitions: Factorial
 0! = 1
 n! = n x (n-1)!

 If a recursive function never reaches its base case,


a stack overflow error occurs

GIT – Computer Engineering Department Simulation - Lecture Notes 8 11


Recursive Definitions: Factorial Code

int factorial (int n) {


if (n == 0) // or: throw exc. if < 0
return 1;
else
return n * factorial(n-1);
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 12


Recursive Definitions: Power
 x0 = 1
 xn = x × xn-1

double power(double x, int n) {


if (n <= 0) // or: throw exc. if < 0
return 1;
else
return x * power(x, n-1);
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 13


Recursive Definitions: Greatest Common Divisor

Definition of gcd(m, n), for integers m > n > 0:


 gcd(m, n) = n, if n divides m evenly
 gcd(m, n) = gcd(n, m % n), otherwise

int gcd (int m, int n) {


if (m < n)
return gcd(n, m);
else if (m % n == 0) // could check n>0
return n;
else
return gcd(n, m % n);
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 14


Recursive Definitions: Fibonacci Series
Definition of fibi, for integer i > 0:

 fib1 = 1

 fib2 = 1

 fibn = fibn-1 + fibn-2, for n > 2

GIT – Computer Engineering Department Simulation - Lecture Notes 8 15


Fibonacci Series Code
int fib (int n) {
if (n <= 2)
return 1;
else
return fib(n-1) + fib(n-2);
}

This is straightforward, but an inefficient recursion ...

GIT – Computer Engineering Department Simulation - Lecture Notes 8 16


Efficiency of Recursion: Inefficient Fibonacci

# calls apparently
O(2n) – big!

GIT – Computer Engineering Department Simulation - Lecture Notes 8 17


Efficient Fibonacci
 Strategy: keep track of:
– Current Fibonacci number
– Previous Fibonacci number
– # left to compute

GIT – Computer Engineering Department Simulation - Lecture Notes 8 18


Efficient Fibonacci: Code
int fibonacci_start (int n) {
return fibo(1, 0, n);
}

int fibo (int curr, int prev, int n) {


if (n <= 1)
return curr;
else
return fibo(curr+prev, curr, n-1);
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 19


Efficient Fibonacci: A Trace

Performance is O(n)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 20


Recursive Array Search
 Can search an array using recursion
 Simplest way is linear search
– Examine one element at a time
– Start with the first element, end with the last
 One base case for recursive search: empty array
– Result is -1 (negative, not an index  not found)
 Another base case: current element matches target
– Result is index of current element
 Recursive case: search rest, without current element

GIT – Computer Engineering Department Simulation - Lecture Notes 8 21


Recursive Array Search: Linear Algorithm

1. if the array is empty


2. return -1
3. else if first element matches target
4. return index of first element
5. else
6. return result of searching rest of the array,
excluding the first element

GIT – Computer Engineering Department Simulation - Lecture Notes 8 22


Linear Array Search Code
template<typename Item_Type>
int linear_search (
const std::vector<Item_Type>& items,
const Item_Type& target) {
return linear_search(items, target, 0);
}

template<typename Item_Type>
int linear_search (
const std::vector<Item_Type>& items,
const Item_Type& target, size_t pos_first) {
if (pos_first == items.size()) return -1;
else if (target == items[pos_first])
return pos_first;
else
return linear_search(items, target, pos_first+1);
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 23


Array Search: Getting Better Performance

 Item not found: O(n)


 Item found: n/2 on average, still O(n)
 How can we perhaps do better than this?
– What if the array is sorted?
– Can compare with middle item
– Get two subproblems of size ≤ n/2
 What performance would this have?
– Divide by 2 at each recursion  O(log n)
– Much better!

GIT – Computer Engineering Department Simulation - Lecture Notes 8 24


Binary Search Algorithm
 Works only on sorted arrays!
 Base cases:
– Empty (sub)array
– Current element matches the target
 Check middle element with the target
 Consider only the array half where target
can still lie

GIT – Computer Engineering Department Simulation - Lecture Notes 8 25


Binary Search Algorithm Steps
1. if array is empty
2. return -1 as result
3. else if middle element matches
4. return index of middle element as result
5. else if target < middle element
6. return result of searching lower portion of array
7. else
8. return result of searching upper portion of array

GIT – Computer Engineering Department Simulation - Lecture Notes 8 26


Binary Search Example

GIT – Computer Engineering Department Simulation - Lecture Notes 8 27


Binary Search Code
template <typename Item_Type>
int binary_search(const std::vector<Item_Type>& items,
int first, int last, const Item_Type& target) {
if (first > last)
return -1; // Base case for unsuccessful search.
else {
int middle = (first + last) / 2; // Next probe index.
if (target < items[middle])
return binary_search(items, first, middle - 1,
target);
else if (items[middle] < target)
return binary_search(items, middle + 1, last,
target);
else
return middle; // Base case for successful search.
}
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 28


Binary Search Code (2)
template <typename Item_Type>
int binary_search(const std::vector<Item_Type>items,
const Item_Type& target) {
return binary_search(items,
0, items.size() - 1, target);
}

 C++ Standard library function binary_search


defined in <algorithms> does this.

GIT – Computer Engineering Department Simulation - Lecture Notes 8 29


Problem Solving with Recursion
 Towers of Hanoi
 Counting grid squares in a blob
 Backtracking, as in maze search

GIT – Computer Engineering Department Simulation - Lecture Notes 8 30


Towers of Hanoi: Description
Goal: Move entire tower to another peg
Rules:
1. You can move only the top disk from a peg.
2. You can only put a smaller on a larger disk (or on
an empty peg)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 31


Towers of Hanoi: Solution Strategy

GIT – Computer Engineering Department Simulation - Lecture Notes 8 32


Towers of Hanoi: Solution Strategy (2)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 33


Towers of Hanoi: Program Specification

Problem Inputs
Number of disks (an integer)
Letter of starting peg: L (left), M (middle), or R (right)
Letter of destination peg (L, M, or R), but different from starting peg.
Letter of temporary peg (L, M, or R), but different from starting peg and destination peg.
Problem Outputs
A list of moves

GIT – Computer Engineering Department Simulation - Lecture Notes 8 34


Towers of Hanoi: Program Specification (2)

Function Behavior
void show_moves(int n, char Builds a string containing all moves for a
start_peg, char dest_peg, chat game with n disks on start_peg that will
temp_peg) be moved to dest_peg, using temp_peg
for temporary storage of disks being moved.

GIT – Computer Engineering Department Simulation - Lecture Notes 8 35


Towers of Hanoi: Recursion Structure
move(n, src, dst, tmp) =
if n == 1: move disk 1 from src to dst
otherwise:
move(n-1, src, tmp, dst)
move disk n from src to dst
move(n-1, tmp, dst, src)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 36


Towers of Hanoi: Code
void show_moves(int n, char start_peg,
char dest_peg, char temp_peg)
{
if (n == 1)
{
cout << "Move disk 1 from peg " << start_peg
<< " to peg " << dest_peg << "\n";
}
else
{ // Recursive step
show_moves(n - 1, start_peg, temp_peg, dest_peg);
cout << "Move disk " << n << " from peg " << start_peg
<< " to peg " << dest_peg << "\n";
show_moves(n - 1, temp_peg, dest_peg, start_peg);
}
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 37


Towers of Hanoi: Performance Analysis
How big will the output be for a tower of size n?
We’ll just count lines; call this L(n).
 For n = 1, one line: L(1) = 1
 For n > 1, one line plus twice L for next smaller
size:
L(n+1) = 2 x L(n) + 1

Solving this gives L(n) = 2n – 1 = O(2n)


So, don’t try this for very large n.

GIT – Computer Engineering Department Simulation - Lecture Notes 8 38


Counting Cells in a Blob
 Desire: Process an image presented as a
two-dimensional array of color values
 Information in the image may come from
– X-Ray
– MRI
– Satellite imagery
– Etc.
 Goal: Determine size of any area considered
abnormal because of its color values

GIT – Computer Engineering Department Simulation - Lecture Notes 8 39


Counting Cells in a Blob (2)
 A blob is a collection of contiguous cells that
are abnormal
 By contiguous we mean cells that are
adjacent, horizontally, vertically, or diagonally

GIT – Computer Engineering Department Simulation - Lecture Notes 8 40


Counting Cells in a Blob: Example

GIT – Computer Engineering Department Simulation - Lecture Notes 8 41


Counting Cells in a Blob: Recursive Algorithm

Algorithm count_cells(x, y):


 if (x, y) outside grid
 return 0
 else if color at (x, y) normal
 return 0
 else
 Set color at (x, y) to “Temporary” (normal)
 return 1 + sum of count_cells on neighbors

GIT – Computer Engineering Department Simulation - Lecture Notes 8 42


Count Cells Code
int count_cells(color grid[ROW_SIZE][COL_SIZE], int r, int c) {

if (r < 0 || r >= ROW_SIZE || c < 0 || c >= COL_SIZE)


{
return 0;
}
else if (grid[r][c] != ABNORMAL)
{
return 0;
}
else
{
grid[r][c] = TEMPORARY;
return 1
+ count_cells(grid, r - 1, c - 1) + count_cells(grid, r - 1, c)
+ count_cells(grid, r - 1, c + 1) + count_cells(grid, r, c + 1)
+ count_cells(grid, r + 1, c + 1) + count_cells(grid, r + 1, c)
+ count_cells(grid, r + 1, c - 1) + count_cells(grid, r, c - 1);
}
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 43


Backtracking
 Backtracking: systematic trial and error search for
solution to a problem
– Example: Finding a path through a maze
 In walking through a maze, probably walk a path as
far as you can go
– Eventually, reach destination or dead end
– If dead end, must retrace your steps
– Loops: stop when reach place you’ve been before
 Backtracking systematically tries alternative paths
and eliminates them if they don’t work

GIT – Computer Engineering Department Simulation - Lecture Notes 8 44


Backtracking (2)
 If you never try exact same path more than once, and
 You try all possibilities,
 You will eventually find a solution path if one exists

 Problems solved by backtracking: a set of choices


 Recursion implements backtracking straightforwardly
– Activation frame remembers choice made at that decision point
 A chess playing program likely involves backtracking

GIT – Computer Engineering Department Simulation - Lecture Notes 8 45


Maze Solving Algorithm: findPath(x, y)
1. if (x,y) outside grid, return false
2. if (x,y) barrier or visited, return false
3. if (x,y) is maze exit, color PATH and return true
4. else:
5. set (x,y) color to PATH (“optimistically”)
6. for each neighbor of (x,y)
7. if findPath(neighbor), return true
8. set (x,y) color to TEMPORARY (“visited”)
9. return false

GIT – Computer Engineering Department Simulation - Lecture Notes 8 46


Maze Solving Code
bool findMazePath(color grid[ROW_SIZE][COL_SIZE], int r, int c)
{
if (r < 0 || c < 0 || r >= ROW_SIZE || c >= COL_SIZE)
return false; // Cell is out of bounds.
else if (grid[r][c] != BACKGROUND)
return false; // Cell is on barrier or dead end.
else if (r == ROW_SIZE - 1 && c == COL_SIZE - 1)
{
grid[r][c] = PATH; // Cell is on path
return true; // and is maze exit.
}
else
. . .
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 47


Maze Solving Code (2)
{ // Recursive case.
// Attempt to find a path from each neighbor.
// Tentatively mark cell as on path.
grid[r][c] = PATH;
if (findMazePath(grid, r - 1, c)
|| findMazePath(grid, r + 1, c)
|| findMazePath(grid, r, c - 1)
|| findMazePath(grid, r, c + 1 ) )
{
return true;
}
else
{
grid[r][c] = TEMPORARY; // Dead end.
return false;
}
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 48


Recursion Versus Iteration
 Recursion and iteration are similar
 Iteration:
– Loop repetition test determines whether to exit
 Recursion:
– Condition tests for a base case
 Recursive method often slower than iterative; why?
– Overhead for loop repetition smaller than overhead for call and return
 If easier to develop algorithm using recursion,
– Then code it as a recursive method (simpler than iterative)
• easier to write, read, and debug
– Software engineering benefit probably outweighs ...
• reduction in efficiency

GIT – Computer Engineering Department Simulation - Lecture Notes 8 49


Recursion Versus Iteration
 Recursive subroutines are implemented using stacks
– Programmer who use recursion does not have to think about stacks
– This is done automatically
 Remember the string size example:

GIT – Computer Engineering Department Simulation - Lecture Notes 8 50


Recursion  Iteration
 It is possible for a programmer to implement a
recursive algorithm iteratively “by hand”
– using a stack structure
– a non-recursive routine might be faster than a recursive
routine
 Advantages
– have advantages of iterative solutions
– no additional function calls
– protect from stack overflow
– run faster
– use less memory

GIT – Computer Engineering Department Simulation - Lecture Notes 8 51


Recursion  Iteration
 When recursion involves single call that is at the end ...
 It is called tail recursion and it easy to make iterative:

GIT – Computer Engineering Department Simulation - Lecture Notes 8 52


Example: Print an array of integers
void Print(int X[],int N) {
if (N == 0)
cout << X[0] << "\n"; //stopping case
else {
cout << X[N] << "\n";
PrintBack(X, N - 1); //recursive step
}
}

void Print(int X[],int N) {


while(N > 0){
cout << X[N] << "\n";
N = N - 1;
}
cout << X[0] << "\n";
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 53


Example: String length
int size(string str) {
if (str == "")
return 0;
else
return 1 + size(str.substr(1));
}

int iterSize(string str) {


int len = 0;
while (str == ""){
len=len+1
str = str.substr(1);
}
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 54


Example: Factorial
int factorial (int n) {
if (n == 0) // or: throw exc. if < 0
return 1;
else
return n * factorial(n-1);
}

int iterFact (int n) {


int result = 1;
for (int k = 1; k <= n; k++) {
result = result * k;
}
return result;
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 55


Example: Binary Search
template <typename Item_Type>
int binary_search(const std::vector<Item_Type>& items,
int first, int last, const Item_Type& target) {
int middle;
while (first <= last){
middle = (first + last) / 2;
if (target < items[middle])
middle = middle - 1;
else if (items[middle] < target)
first = middle + 1;
else
return middle;
}
return -1;
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 56


Example: Binary Search
template <typename Item_Type>
int binary_search(const std::vector<Item_Type>& items,
int first, int last, const Item_Type& target) {
if (first > last)
return -1; // Base case for unsuccessful search.
else {
int middle = (first + last) / 2; // Next probe index.
if (target < items[middle])
return binary_search(items, first, middle - 1, target);
else if (items[middle] < target)
return binary_search(items, middle + 1, last, target);
else
return middle; // Base case for successful search.
}
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 57


Recursion  Iteration: using a stack
 If not tail recursion but recursion is at the end:

GIT – Computer Engineering Department Simulation - Lecture Notes 8 58


Example: String length
int size(string str) {
{
ParamStack.Push(str);
while (!ParamStack.IsEmpty()){
ParamStack.Pop(str);
if (str == "")
len =0;
else {
len=len+1;
ParamStack.Push(str.substr(1));
}
} int size(string str) {
} if (str == "")
return 0;
else
return 1 + size(str.substr(1));
}
GIT – Computer Engineering Department Simulation - Lecture Notes 8 59
Example: Print an array of integers
void Print(int X[],int N) {
ParamStack.Push(N);
while (!ParamStack.IsEmpty()){
ParamStack.Pop(N);
if(N == 0)
cout << X[0] << "\n";
else {
cout << X[N] << "\n";
ParamStack.Push(N - 1);
} void Print(int X[],int N) {
} if (N == 0)
}
cout << X[0] << "\n";
else {
cout << X[N] << "\n";
PrintBack(X, N - 1);
}
}
GIT – Computer Engineering Department Simulation - Lecture Notes 8 60
Towers of Hanoi (Recursive)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 61


Towers of Hanoi (Iterative)

GIT – Computer Engineering Department Simulation - Lecture Notes 8 62


Other Examples
 Counting Cells in a Blob
– Can we use the same method?
 What about maze solving?
– Recursive calls are not at the end?

GIT – Computer Engineering Department Simulation - Lecture Notes 8 63


Example: Print an array elements in reverse
printback(a,n)
if(n==0) return
else
printback(a+1,n-1)
print(*a)

printback(a,n)
Stack s;
s.push(a,n,0)
while s.notempty
a,n,i=s.pop()
if(n==0) continue
if(i==0)
s.push(a,n,i+1)
s.push(a+1,n-1,0)
else if(i==1) print(*a)
else ERROR

GIT – Computer Engineering Department Simulation - Lecture Notes 8 64


Example: Counting nodes in a tree
// Counts the number of nodes in a tree

Recursive count:
int count(TreeNode *t)
{
int a=0;
if t==NULL return 0;
else{
a+=count(t->left);
a+=count(t->right);
a=a+1;
return a;
}
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 65


Example: Counting nodes in a tree
Iterative count: else{
s.push(t,0,0,NULL) if(i==0){
while stack not empty s.push(t,a,i+1,r)
{ s.push(t->left,0,0,NULL)
t,a,i,r=s.pop() }
if(t==NULL){ else if(i==1){
t’,a’,i’,r’=s.pop() a=a+r
r’=0 s.push(t,a,i+1,NULL)
s.push(t’,a’,i’,r’) s.push(t->right,0,0,NULL)
continue }
} else if(i==2){
a=a+r
a=a+1
t’,a’,i’,r’=s.pop()
r’=a
s.push(t’,a’,i’,r’)
}
}
}

GIT – Computer Engineering Department Simulation - Lecture Notes 8 66

You might also like