An Introductgfdion To Recursion
An Introductgfdion To Recursion
Introduction Recursion is a programming technique where a function calls itself. While this may sound like an error, the design of C/C++ function calls at runtime make it not only legal, but incredibly useful. Recursion is used to break a problem up into smaller, similar problems so that they might be sol ed easier. !he canonical first e"ample of recursion is a function for calculating factorials. A factorial is a number that is defined as N! = N * (N - 1)!, for N > 0 #actorials are defined recursi ely, the abo e formula basically says that !he factorial of $ is $ times the factorial of $ % & as long as $ is greater than '. (n C/C++ we could do this using a loop, like so)
int fact_iterative ( int i ) { int ret = 1; while ( i > 0 ) ret *= i--; return ret; }
*imple enough, fact+iterati e , - . e"ecutes like this) 5 * 4 * 3 * 2 * 1 = 120 which is the correct answer. We can write a function that is closer to the definition of a factorial by ha ing our function call itself, like this)
$otice that fact+recursi e calls itself with i % & and then multiplies the return alue by i. !his function is ery similar to the factorial definition abo e, and that makes it easier to check for errors. Base Cases $otice that a test for i / ' is made before the function is called again. !his is called the base case, and it is required for e ery recursi e function. Without a base case, the function would not know when to stop calling itself, and you would ha e an infinite recursion ,or until you ran out of stack space.. *peaking of stack space, how does recursion work anyway0 1ood question.
2 ery time you call a function, a thingy , ery technical term. called an acti ation record is pushed onto the stack. !he acti ation record, also known as a stack frame, contains things such as local ariables for the function, arguments, and the address of the pre ious acti ation record so that it knows how to return from whence it came. 3ecause of these acti ation records, two calls to the same function aren4t actually the same acti ation record, that4s why recursion works. 2ach new call to fact+recursi e has its own memory, its own argument copies, its own e erything. 3ecause this all works like a stack, each new acti ation record co ers up the pre ious record. When the function finally reaches the base case and returns, each acti ation record is popped from the stack until e"ecution finally lea es the first call to fact+recursi e. !his is called unwinding the stack, you4ll hear that term again sometime in your programming careers, but ( won4t say where. ). Binary Search 5ost recursi e solutions will follow the di ide and conquer concept. Where one di ides the problem in half ,and each sub%problem in half. until the solution is ob ious. !he best e"ample of this is a binary search)
int binsearch ( int *a, int l, int h, int key ) { int m; m = ( l + h ) / 2; if ( a[m] == key ) return m; else if ( l > h ) return -1; else if ( a[m] < key ) return binsearch ( a, m + 1, h, key ); else return binsearch ( a, l, m - 1, key ); }
3y di iding the array in half, it4s easy to determine which half the desired element is in. !hen that half is di ided, and the half that results, and so on until the only option left is a single element. #rom that single element it is tri ial to determine if the search key is in the array. ). ( encourage you to play with this function until you fully understand how it works. Simulating Recursion Any operation that can be carried out with recursion can also be carried out with an e"plicit stack, that is, a stack that you write can be used to simulate recursion. !aking the factorial e"ample because (4m too la6y to write a stack based binary search routine, we could come up with something like this)
int fact_stack ( int i ) { int stack[5]; /* Assume fact_stack ( 5 ) */ int top = 0; int ret = 1; while ( i > 0 ) stack[top++] = i--; while ( top > 0 )
(t really isn4t much different from the iterati e ersion e"cept for the use of a stack. $ow, while it4s certainly possible to simulate recursion, as e idenced by the abo e function, it4s rarely practical. (f you can con ert the recursi e function to an iterati e function easily, there4s no point in simulating recursion. (f a stack is required for correct operation, such as with an iterati e quicksort, the e"tra work in ol ed makes the iterati e approach impractical e"cept in e"treme cases of performance. Tail Recursion !here is one kind of recursion that can easily be con erted into iteration, and usually should ha e been iterati e to begin with. !his is called tail recursion. !ail recursion is a special case where a single recursi e call to the function is at the end of the same function, in other words)
!ail recursion is usually optimi6ed by compilers, but you can do it yourself simply by turning the recursion into a loop. !his is ery simple) void tail ( int i ) { while ( i != 0 ) i--; }
Performance Issues Recursion isn4t always the best choice. (n fact, it isn4t the best choice most of the time because both the drastic growing of the stack and the o erhead of function calls tends to effect efficiency more than is practical. *ome algorithms, such as binary tree tra ersals and recursi e sorting routines are better suited recursi ely. 7owe er, these algorithms ha e ery precise growth needs or would be difficult and awkward to implement iterati ely.