330 likes | 385 Views
Dive into the world of recursion with detailed explanations, examples, and terminology. Learn about iterative versus recursive approaches, types of recursion, and practical applications through code snippets.
E N D
Recursion There are two kinds of people in the world, those who divide the world into two kinds of people and those who do not.
Recursion Terminology • Types of Recursion • direct • indirect • mutual • tail • linear • tree
Recursion Terminology (con’t) • stack (LIFO) • push, pop • memory map • runtime stack • activation record • stack overflow (stack/heap collision)
Iterative Factorial int factorial(int n) // Precondition: n >= 0 { int i, result; result = 1; for (i = 1; i <= n; i++) { result = result * i; } return (result); }
Recursive Factorial int factorial(int n) // Precondition: n>=0 { if (n == 0) return (1); return (n * factorial(n - 1) ); }
Iterative Power double power(double number, int exponent) // Precondition: exponent >= 0 { double p = 1; for (int i = 1; i <= exponent; i++) p = p * p; return (p); }
Recursive Power double power(double number, int exponent) // Precondition: exponent >= 0 { if (exponent == 0) return (1); return (number * power(number, exponent-1)); }
Recursive String Reversal void reverseString(int length, char s[]) { if (length < 0) return; cout << s[length]; reverseString(length-1, s); }
Recursive Summing of Integers int sumNumbers(int m, int n) // Precondition: m <= n { int total; if (m == n) return (n); else total = m + sumNumbers(m+1, n); return (total); }
Recursive Nonsense #include <iostream.h> int thing(int); void tap(int); void cat(int); void dog(int, int); void main() { int a, b; a = 5; b = thing(a); cout << b << endl;
Recursive Nonsense (con’t) a = 1; tap(a); b = 4; cat(b); b = 1; a = 5; dog(b, a); }
Recursive Nonsense (con’t) int thing(int n) { int x; if (n == 2) return(1); else { x = thing(n-1) + 2; cout << x << endl; return(x); } }
Recursive Nonsense (con’t) void tap(int n) { if (n != 5) { cout << n << endl; tap(n+1); cout << n << endl; } else cout << n << endl; }
Recursive Nonsense (con’t) void cat(int x) { if (x == 2) cout << x << endl; else { cat(x-1); cout << x << endl; } }
Recursive Nonsense (con’t) void dog(int x, int n) { if (x >= n) cout << “Hello\n”; else { cout << x << ‘\t’ << n << endl; dog(x+1, n-1); } }
Approach to Writing Recursive Functions • Using “factorial” as an example, • Write the function header so that you are sure • what the function will do and how it will be called. • [For factorial, we want to send in the integer • for which we want to compute the factorial. • And we want to receive the integer result back.] • int factorial(int n)
Approach (con’t) 2. Decompose the problem into subproblems (if necessary). [For factorial, there are no subproblems. We will see examples of subproblems later.] 3. Write recursive calls to solve those subproblems whose form is similar to that of the original problem. [Again, we will see subproblem examples later. But there is a recursive call to write: factorial(n-1) ]
Approach (con’t) 4. Write code to combine, augment, or modify the results of the recursive call(s) if necessary to construct the desired return value or create the desired side effects. [When a factorial has been computed, the result must be multiplied by the current value of the number for which the factorial was taken: return (n * factorial(n-1)) ]
Approach (con’t) 5. Write base case(s) to handle any situations that are not handled properly by the recursive portion of the program. [The base case for factorial is that if n=0, the factorial is 1: if (n == 0) return(1); ]
Does It Work? • Using “factorial” as an example, • 1. A recursive subprogram must have at • least one base case and one recursive • case (it's OK to have more than one base • case and/or more than one recursive case). • [ The first condition is met, because if • (n==0) return 1 is a base case, while the • "else" part includes a recursive call • (factorial(n-1)). ]
Does It Work? (con’t) 2. The test for the base case must execute before the recursive call. [ If we reach the recursive call, we must have already evaluated if (n==0); this is the base case test, so this criterion is met. ]
Does It Work? (con’t) • 3. The problem must be broken down in such • a way that the recursive call is closer to the • base case than the top-level call. • [ The recursive call is factorial(n-1). The argument • to the recursive call is one less than the argument • to the top-level call to factorial. It is, therefore, • “closer” to the base case of n=0.]]
Does It Work? (con’t) • 4. The recursive call must not skip over the • base case. • [ Because n is an integer, and the recursive • call reduces n by just one, it is not possible • to skip over the base case. ]
Does It Work? (con’t) • 5. The non-recursive portions of the subprogram • must operate correctly. • [ Assuming that the recursive call works properly, • we must now verify that the rest of the code • works properly. We can do this by comparing the • code with our definition of factorial. This definition • says that if n=0 then n! is one. Our function correctly • returns 1 when n is 0. If n is not 0, the definition • says that we should return (n-1)! * n. The recursive • call (which we now assume to work properly) • returns the value of n-1 factorial, which is then • multiplied by n. Thus, the non--recursive portions • of the function behave as required. ]
Recursion vs. Iteration • Recursion • Usually less code – algorithm can be easier to follow (Towers of Hanoi, binary tree traversals, flood fill) • Some mathematical and other algorithms are naturally recursive (factorial, summation, series expansions)
Recursion vs. Iteration (con’t) • Iteration • Use when the algorithms are easier to write, understand, and modify (especially for beginning programmers) • Generally, runs faster because there is no stack I/O • Sometimes are forced to use iteration because stack cannot handle enough activation records (power(2, 5000))
Fibonacci Numbers The Fibonacci numbers can be defined by the rule: fib(n) = 0 if n is 0, = 1 if n is 1, = fib(n-1) + fib(n-2) otherwise For example, the first seven Fibonacci numbers are Fib(0) = 0 Fib(1) = 1 Fib(2) = Fib(1) + Fib(0) = 1 Fib(3) = Fib(2) + Fib(1) = 2 Fib(4) = Fib(3) + Fib(2) = 3 Fib(5) = Fib(4) + Fib(3) = 5 Fib(6) = Fib(5) + Fib(4) = 8
Tree Recursive Fibonacci int fib(int n) // Precondition: n >= 0 { if (n == 0) return 0; if (n == 1) return 1; return (fib(n - 1) + fib(n - 2) ); }
Tail Recursive Fibonacci int fib_aux(int n, int next, int result) // Precondition: n >= 0 { if (n == 0) return result; return (fib_aux(n - 1, next + result, next) ); } int fib(int n) { return fib(n, 1, 0); }
Linear Recursive Count_42s int count_42s(int array[], int n) { if (n == 0) return 0; if (array[n-1] != 42) { return (count_42s(array, n-1) ); } return (1 + count_42s(array, n-1) ); }
Tree Recursive Count_42s int count_42s(int array[], int low, int high) { if ((low > high) || (low == high && array[low] != 42)) { return 0 ; } if (low == high && array[low] == 42) { return 1; } return (count_42s(array, low, (low + high)/2) + count_42s(array, 1 + (low + high)/2, high)) ); }
Floodfill void Image::floodfill(int x, int y) { if (x < 0 || x >= numRows || y < 0 || y >= numCols) return; if (a[x][y] == '1') return; if (a[x][y] == '0') a[x][y] = '1'; floodfill(x+1, y); // Go up floodfill(x-1, y); // Go down floodfill(x, y-1); // Go left floodfill(x, y+1); // Go right }