Page 46 - DCAP201_FUNDAMENTALS_OF_DATA_STRUCTURES
P. 46

Unit 3: Recursion




             For recursion to be successful, the problem needs to have a recursive substructure. This is  Notes
             a fancy term that says that to solve a big problem (say of size N), you should be able to
             solve a smaller problem (of smaller size) in exactly the same way, except the problem size
             is smaller.

             As an analogy, you might have to, say, sweep the floor. Sweeping half the floor is the same
             as sweeping the entire floor, except you do it on a smaller area. A problem that is solved
             recursively generally needs to have this property.
             Many folks think recursive functions are implemented in some weird and unusual way in
             assembly language. They can’t believe it’s implemented just like any other function.

             The biggest misconception is that a function calls a smaller and smaller version, reaches
             the base case, then quits. For example, you might call fact(3), which calls fact(2), which
             calls fact(1), which finally calls fact(0), the base case.
             Some programmers think the function stops there, right at the base case, and then jumps
             back to the initial recursive call. But you know that’s not true.

             Or do you? Suppose we have function f(), which calls function g(), and g() calls h().
             What happens when we’re done with h()? We return back to g(). And what happens when
             we’re done with g()? We return back to f().

             Recursive functions behave that way too! Thus, fact(3) calls fact(2) which calls fact(1)
             which calls fact(0), the base case. We call this phase the winding of the stack.
             Once fact(0) is done, it goes back to fact(1), just like it would for non-recursive functions.
             When fact(1) is done, it goes back to fact(2). When fact(2) is done, it goes back to fact(3).
             When fact(3) is done, it goes back to whoever called fact(3). We call this the “unwinding”
             of the stack.
             During the winding of the stack, you are making progress towards the base case. Basically,
             you’re trying to get to the base case, solve the base case, and slowly grow your solution
             back as you go though the unwinding part of the recursion. Thus, winding heads to the
             solution of the base case, while unwinding typically grows the solution from base case
             back to the original call.
             An Example

             Let’s solve the following recursive function, written in C. This sums all elements of an
             array.
             int sum( int arr[], int size ) {
              if ( size == 0 )
              return 0 ;
              else
              return sum( arr, size - 1 ) + arr[ size - 1 ] ;
             }

             A MIPS Translation
             We assume arr is in $a0 and size is in $a1.
             We have to decide what to save to the stack. We have two choices. Either save size – 1, from
             which we can compute arr[ size – 1 ], or save arr[ size – 1 ]. Let’s opt to save size – 1 on the
             stack.

                                                                                 Contd...



                                           LOVELY PROFESSIONAL UNIVERSITY                                   39
   41   42   43   44   45   46   47   48   49   50   51