Download Presentation
## Data Structures

- - - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - - -

**Andreas Savva**Data Structures Chapter 5 Recursion**M**A D B C D D D Stack Frames for Subprograms Time Tree of Subprogram Calls Finish Start**Tree-Diagram Definitions**• The circles in a tree diagram are called verticesor nodes. • The top of the tree is called its root. • The vertices immediately below a given vertex are called the childrenof that vertex. • The (unique) vertex immediately above a given vertex is called its parent. (The root is the only vertex in the tree that has no parent) • The line connecting two vertices is called a branch. • Siblingsare vertices with the same parent. • A vertex with no children is called a leafor an external vertex. • Two branches of a tree are adjacentif the lower vertex of the first branch is the upper vertex of the second. A sequence of branches in which each is adjacent to its successor is called a path. • The heightof a tree is the number of vertices on a longest possible path from the root to a leaf. (Hence a tree containing only one vertex has height 1.) • The depthor levelof a vertex is the number of branches on a path from the root to the vertex.**Recursion**Every recursive process consists of two parts: • A base case that is processed without recursion. • A general method that reduces a particular case to one or more of the smaller cases, making progress towards eventually reducing the problem all the way to the base case.**Mathematical Recursion**• Base case: f(0) = 0 • Recursion: f(n) = n + f(n-1) for n > 0 f(5) = 5 + f(4) = 5 + 4 + f(3) = 5 + 4 + 3 + f(2) = 5 + 4 + 3 + 2 + f(1) = 5 + 4 + 3 + 2 + 1 + f(0) = 5 + 4 + 3 + 2 + 1 + 0 = 15**A Recursive Definition - Factorial**int factorial(int n) // Pre: n is a nonnegative integer // Post:Return the value of the factorial of n { if (n == 0) return 1; else return n * factorial(n - 1); }**Memory**n n n n n f(0) f(4) f(1) f(3) f(2) 0 2 1 4 3 main() 24 Factorial(4) int factorial(int n) { if (n == 0) return 1; else return n * factorial(n-1); } void main() { cout << factorial(4); } 1 = 1 = 1 * 1 = 1 = 1* f(0) = 2 * f(1) = 2 * 1 = 2 2 = 3 * f(2) = 3 * 2 = 6 6 = 4 * 6 = 24 = 4 * f(3) 24**Memory**n n n n n Display Display Display Display Display -999 8 1 3 12 main 1 3 12 8 -999 <enter> Recursive Procedure 8 12 3 1 void Display() { int n; cin >> n; if (n != -999) { Display(); cout << n << ’ ’; } } void main() { Display(); }**Memory**c c n c n c n c n n display display display display display ’B’ 2 ’A’ 3 ’E’ 4 1 ’D’ 0 ’C’ main Recursive Procedure #include <iostream.h> void display(int n, char c) { if (n != 0) { cout << c; display(n-1,c+1); } } void main() { display(4,’A’); } A B C D**123456789**1 2 3 4 5 6 7 Exercise #include <iostream.h> void display(int n, int m, char c) { if (n==1 && m==1) cout << c; else if (n > 1) { cout << endl; display(n-1,m,c); } else if (m > 1) { cout << ’ ’; display(n,m-1,c); } } void main() { display(3,4,’7’); }**Function Power –nr**Base case: Key step: int power(int n, int r) // Pre: r is a nonnegative integer // Post:Return the value of n to the power r { if (r == 0) return 1; else return n * power(n, r - 1); }**Memory**n n n r r r r n r n f(2,3) f(2,2) f(2,4) f(2,0) f(2,1) 2 4 2 2 3 2 0 2 1 2 main() 16 Function Power #include <iostream.h> int power(int n, int r) { if (r == 0) return 1; else return n * power(n,r-1); } void main() { cout << power(2,4); } = 1 1 = 2 * f(2,0) = 2 * 1 = 2 2 = 2 * 2 = 4 = 2 * f(2,1) 4 = 2 * 4 = 8 = 2 * f(2,2) 8 = 2 * 8 = 16 16 = 2 * f(2,3)**Divide and Conquer: The Towers of Hanoi**• Developed in the 19th century. • The problem: At the creation of the world, the priests in the temple of Brahma were given a brass platform on which were 3 diamond needles. On the first needle there were stacked 64 golden disks, each one slightly smaller than the one under it. The priests were assigned the task of moving all the golden disks from the first needle to the third, subject to the conditions that: • only one disk can be moved at a time, and • no disk is allowed to be placed on top of a smaller disk. The priest were told that when they had finished moving the 64 disks, it would be the end of the world.**1**2 3 The Towers of Hanoi move(64,1,3,2) means “Move 64 disks from tower 1 to tower 3 using tower 2 as temporary**1**2 3 4 . . . 62 63 1 2 3 4 . . . 62 63 1 2 3 4 . . . 62 63 The Solution move(63,1,2,3) // Move 63 disks from tower 1 to tower 2 cout << ”Move disk 64 from tower 1 to tower 3” << endl; move(63,2,3,1) // Move 63 disks from tower 2 to tower 3 1 2 3 64 64**2**3 1 disks = 3 The Program const int disks = 64; void move(int count, int start, int finish, int temp) { if (count > 0) { move(count – 1, start, temp, finish); cout << ”Move disk ” << count << ” from ” << start << ” to “ << finish << endl; move(count – 1 , temp, finish, start); } } int main() { move(disks, 1, 3, 2); return 0; }**move(3,1,3,2)**move(2,1,2,3) move(2,2,3,1) move(1,3,2,1) move(1,1,3,2) move(1,1,3,2) move(1,2,1,3) move(0,3,1,2) move(0,2,3,1) move(0,1,2,3) move(0,1,2,3) move(0,1,2,3) move(0,3,1,2) move(0,2,3,1) move(0,2,3,1) Analysis (disks = 3) Move disk 1 from tower 1 to tower 3 Move disk 2 from tower 1 to tower 2 Move disk 1 from tower 3 to tower 2 Move disk 3 from tower 1 to tower 3 Move disk 1 from tower 2 to tower 1 Move disk 2 from tower 2 to tower 3 Move disk 1 from tower 1 to tower 3 move(0,2,3,1) move(0,2,3,1) move(0,3,1,2) move(0,1,2,3) move(0,3,1,2) move(0,1,2,3) move(0,2,3,1) move(0,1,2,3) move(1,1,3,2) move(1,2,1,3) move(1,3,2,1) move(1,1,3,2) move(2,2,3,1) move(2,1,2,3) move(3,1,3,2)**No of Moving Disks (disks=64)**• No of recursions: 1 + 2 + 4 + … + 264= 20 + 21 + 22 + … + 264= 265 – 1 = 36,893,488,147,419,103,231 • No of moving disks: • Excluding the calls with count == 0, which do nothing: 20 + 21 + 22 … + 263= 264 – 1 = 18,446,744,073,709,551,615 • No of Seconds in a year: 32,000,000 • No of years the priests need to solve the problem if they move one disk in every second: 576,460,752,303**Designing Recursive Algorithms**• Find the key step. Begin by asking yourself, “How can this problem be divided into parts?” • Find a stopping rule. This stopping rule is usually the small, special case that is trivial or easy to handle without recursion. • Combine the stopping rule and the key step, using an if-statementto select between them. • Check termination. Verify that the recursion will always terminate. Be sure that your algorithm correctly handles extreme cases. • Draw a recursion tree. The height of the tree is closely related to the amount of memory that the program will require, and the total size of the tree reflects the number of times the key step will be done.**When not to use Recursion**Consider the following two functions for calculating factorial: Recursive: int factorial(int n) { if (n == 0) return 1; else return n * factorial(n − 1); } Non-recursive: int factorial(int n) { int count, product = 1; for (count = 1; count <= n; count++) product *= count; return product; }**factorial(5) - Recursive**Example: factorial(5) = 5 * factorial(4) = 5 * (4 * factorial(3)) = 5 * (4 * (3 * factorial(2))) = 5 * (4 * (3 * (2 * factorial(1)))) = 5 * (4 * (3 * (2 * (1 * factorial(0))))) = 5 * (4 * (3 * (2 * (1 * 1)))) = 5 * (4 * (3 * (2 * 1))) = 5 * (4 * (3 * 2)) = 5 * (4 * 6) = 5 * 24 = 120**Question**• Which of the two functions uses less storage space? • The recursive one has one variable • The iterative one has three variables • But actually the recursive program will set up a stack and fill it in with n+1 numbers: n, n-1, n-2, … , 2, 1, 0 • Thus, the recursive function keeps more storage, and it will take more time as well, since it must store and retrieve all the numbers as well as multiply them.**Fibonacci recursive function**Definition: • Fibonacci numbers are denoted by the recurrence relation F0 = 0, F1 = 1, Fn= Fn−1 + Fn−2 for n ≥ 2 term int fibonacci(int n) { if (n == 0) return 0; else if (n == 1) return 1; elsereturn fibonacci(n-1) + fibonacci(n-2); }**fibonacci(7)**No of calculations of F(n) grows exponentially with n**Fibonacci iterative function**int fibonacci(int n) { } No of calculations of F(n) increases linearly in n**Towers of Hanoi without tail recursion**const int disks = 64; void move(int count, int start, int finish, int temp) { int swap; // temporary storage to swap towers while (count > 0) { // Replace the if statement with a loop. move(count – 1, start, temp, finish); //first recursive call cout << ”Move disk ” << count << ” from ” << start << ” to “ << finish << endl; count--; //Change parameters to mimic the second recursive call. swap = start; start = temp; temp = swap; } } int main() { move(disks, 1, 3, 2); return 0; }**Towers of Hanoi (disks=64)**• With one recursive call: • No of recursions = 64 + 63 + 62 + … + 1 = 2080 • No of variables = No of Recursions * 5 = 10,400 • No of variables needed at one time = tree height * 5 = 64 * 5 = 320 • With two recursive calls: • No of recursions = 36,893,488,147,419,103,231 • No of variables = No of Recursions * 4 = 147,573,952,589,676,412,924 • No of variables needed at one time = tree height * 4 = 65 * 4 = 260**Exercise 1**• What is the output of the following program? #include <iostream> using namespace std; void display(int n) { if (n != 0) { cout << n; display(n-1); cout << n; } } void main() { display(5); }**Exercise 2**• Write a recursive function “Sum” that will take an integer number n and it will return the summation of all the numbers between 1 and n. If n≤ 0 the function should return zero. Examples: • Sum(4) = 1 + 2 + 3 + 4 = 10 • Sum(0) = 0 • Sum(-4) = 0 • Write the same function without recursion.**Exercise 3**• Write a recursive function “Summation” that will take two integer numbers n and m and it will return the summation of all the numbers between n and m. If n > m the function should return zero. Examples: • Summation(3, 7) = 3 + 4 + 5 + 6 + 7 = 25 • Summation(7, 2) = 0 • Summation(4,4) = 4 • Summation(-3,1) = -5 • Write the same function without recursion.**Exercise 4**• Write a recursive procedure “Display” that will take an integer number n and display all the numbers between 1 and n in reverse order. If n≤ 0 it should not display anything. Examples: • Display(5); will display: 5 4 3 2 1 • Display(3); will display: 3 2 1 • Write the same function without recursion.**Exercise 5**• Write a recursive function “Multiply” that will take two integer numbers n and m and it will return the multiplication of all the numbers between n and m. If n > m the function should return zero. Examples: • Multiply(2, 5) = 2 × 3 × 4 × 5 = 120 • Multiply(7, 2) = 0 • Multiply(4, 4) = 4 • Multiply(-3, 1) = 0 • Write the same function without recursion.**Exercise 6**• Write a recursive function “SumEven” that will take two integer numbers n and m and it will return the summation of all the even numbers between n and m. If n > m the function should return zero. Examples: • SumEven(3, 10) = 4 + 6 + 8 + 10 = 28 • SumEven(7, 2) = 0 • SumEven(4,4) = 4 • SumEven(3,3) = 0 • Write the same function without recursion.**Exercise 7**• The greatest common divisor (gcd) of two positive integers is the largest integer that divides both of them. i.e. gcd(8,12) = 4 gcd(9,18) = 9 gcd(16,25) = 1 • Write a non-recursive function, gcd(x,y) that searches through the positive integers until it finds the largest integer dividing both x and y. • Write a recursive function, gcd(x,y) that implements Euclid’s algorithm: if y = 0, then the gcd of x and y is x; otherwise the gcd of x and y is the same as the gcd of y and x % y. • Rewrite the function of part (b) into iterative form. • Discuss the advantages and disadvantages of each of these methods.**Exercise 8**• The binomial coefficients can be defined by the following recurrence relation, which is the idea of Pascal’s triangle. C(n, 0) = 1 C(n, n) = 1 for n≥ 0 C(n, k) = C(n-1, k) + C(n-1, k-1) for n > k > 0**Exercise 8 (continue)**• Write a recursive function to generate C(n, k) by the formula. • Draw the recursion tree for calculating C(6, 4). • Use the square array with n indicating the row and k the column, and write a non-recursive program to generate Pascal’s triangle in the lower left half of the array, that is, in the entries for which k ≤ n. • Write a non recursive program that uses neither an array nor a stack to calculate C(n, k) for arbitrary n ≥ k ≥ 0. • Determine the approximate space and time requirements for each of the algorithms devised in (a), (c), and (d).**Exercise 9**• What is the output of the following program? #include <iostream> using namespace std; void display(int n) { if (n > 0) { display(n-1); cout << n; display(n-1); } } void main() { display(4); }**Exercise 10**• What is the output of the following program? #include <iostream> using namespace std; void display(int n) { if (n > 0) { display(n-1); display(n-1); cout << n; } } void main() { display(4); }**Exercise 11**• What is the output of the following program? #include <iostream> using namespace std; void display(int n) { if (n > 0) { display(n-1); display(n-2); cout << n; } } void main() { display(4); }**Exercise 12**#include <iostream> using namespace std; void display(int n) { if (n > 0) { display(n-1); display(n-2); cout << n; display(n-1); } } void main() { display(4); } • What is the output of the following program?**Exercise 13**• An integer is printed with commas inserted every 3 positions from the right. That is, to print the number 12345678 as 12,345,678, the 678 cannot be printed until after the preceding part of the number is printed. Write a recursive function “PrintWithCommas” that will print its long integer parameter with commas inserted properly. • You must be careful with leading zeros to ensure, for example, that the number 12,003 is printed properly.**Solutions**Backtracking – The Eight-Queen Puzzle • Problem: How to place eight queens on a chessboard so that no queen can take another. • Even the great C. F. Gauss did not obtain a complete solution when he considered it in 1850.**Algorithm**• A more complex recursive application • The choice of data structure can affect a recursive program Example: 4 Queens (4 x 4 Board)**8**1 8 1 8 1 8 1 8 times 8 1 7 1 6 1 1 1 Analysis of Backtracking 8 Queens • Check all the cells • Amount of work = = 4,426,165,368 • Only one queen in each row • Amount of work = … = 88 = 16,777,216 • Only one queen in each column • Amount of work = … = 8! = 40,320 64 8**The Main Program**void main() { int board_size; cout << ”What is the size of the board? ”; cin>> board_size; if (board_size < 1 || board_size > max_board) cout << ”The number must be between 1 and “ << max_board << endl; else { Queens configuration(board_size); solve(configuration); } }**The Recursion**void solve(Queens &configuration) { if (configuration.is_solved()) { confiquration.print(); cout << endl; } else for (int col = 0; col < configuration.board_size; col++) if (configuration.unguarded(col)) { configuration.insert(col); solve(configuration); // Recursively continue to add queens configuration.remove(col); } }**The Queens class**const max_board = 30; class Queens { public: Queens(int size); bool is_solved() const; void print() const; bool unguarded(int col) const; void insert(int col); void remove(int col); int board_size; private: int count; // current number of queens = first unoccupied row bool queen_square[max_board][max_board]; }**The Constructor**Queens::Queens(int size) { board_size = size; count = 0; for (int row = 0; row < board_size; row++) for (int col = 0; col < board_size; col++) queen_square[row][col] = false; }**Is_Solved & Print**bool Queens::is_solved() const { return count == board_size; } void Queens::print() const { for (int row = 0; row < board_size; row++) { for (int col = 0; col < board_size; col++) if (queen_square[row][col]) cout << ”Q”; else cout << ”+”; cout << endl; } } +Q++ +++Q Q+++ ++Q+