6.8.1. Recursion Examples

Instructors and textbooks frequently use two common examples to introduce recursion. The text presents both examples as rules, functions, and C++ functions. A third bonus example illustrates elegant, recursive solutions for visiting or traversing the nodes in a binary tree.

The Factorial Function

The factorial function frequently appears in combinatorial and permutation problems. It is only defined on integers, but the gamma function (not covered here) extends the factorial function to real numbers.

The Rule

0! = 1 (base case)
n! = 1 * 2 * 3 * . . . * (n - 1) * n

Functional Notation

\[ f(n) = \left\{ \begin{array}{ll} 1 & , n = 0 \\ n(n-1) & , n \gt 0 \\ \end{array} \right. \]

The C++ Function

#include <iostream>
using namespace std;

int fact(int n)
{
	if (n > 0)
		return n * fact(n - 1);	// recursion
	else
		return 1;		// non-recursion (base case)
}

int main()
{
	cout << fact(10) << endl;

	return 0;
}
A function to calculate n!. The "false" branch of the if-else-statement implements the non-recursive or base case path through the function. The "true" branch implements the recursive path. Notice that n changes in each recursive call (highlighted).

The Fibonacci Sequence

Few number sequences can match the ubiquitousness of the Fibonacci numbers, which appear everywhere from architecture to biology (see Fibonacci Sequence at Math Is Fun). Can you see how the function implements the rule for forming the Fibonacci sequence?

The Rule

x0 = 0 (base case)
x1 = 1 (base case)
xn = xn-1 + xn-2

Functional Notation

\[ x_n = \left\{ \begin{array}{ll} 0 & , n = 0 \\ 1 & , n = 1 \\ x_{n-1} + x_{n-2} & , n \gt 1 \\ \end{array} \right. \]

The C++ Function

#include <iostream>
using namespace std;

int fib(int n)
{
	if (n == 0)
		return 0;			// base case
	else if (n == 1)
		return 1;			// base case
	else
		return fib(n - 1) + fib(n - 2);	// recursion
}

int main()
{
	cout << fib(10) << endl;

	return 0;
}
A function to find the nth number in the Fibonacci sequence. This implementation of the Fibonacci sequence has two base cases or non-recursive paths through the function. (We could rewrite both the functional notation and the C++ function to collapse the two base cases into one, but, in general, it is possible to have multiple base cases.)

Binary Trees

We explored binary trees in chapter 4 as one use of pointers. We can use binary trees again to demonstrate recursion.

A picture of a balanced binary tree - each side has the same height. The tree has three levels. The top level has one node named A. The second has two nodes named left to right, B and C. The third or bottom level has four nodes, left to right: D, E, F, and G.
struct node
{
	Data	d;
	node*	left;
	node*	right;
};


void display(Data d);
(a)(b)
A binary tree example. Each circle in the picture represents a node, an instance of the node structure. Each node has some data, represented by the letters "A" through "G," and two pointers named left and right, represented by the lines underneath each node. The top node, containing data "A," is called the root, and the nodes at the bottom, "D," "E," "F," and "G," are called leaves.
  1. Binary trees are inherently recursive data structures - any node in the tree is the root of a sub-tree. So, the sub-tree whose root is "B" is also a tree and can be used with any tree operation.
  2. We can conveniently place the structure specification and function prototype in a header file. Furthermore, we are deliberately vague about what we mean by Data and how the display operation works.

The structure specification and the function prototype are sufficient to write a set of small, simple, and quite elegant recursive functions that display the data in a binary tree.

InorderPreorderPostorder
void visit(node* n)
{
	if (n->left != nullptr)
		visit(n->left);
	display(n->d);
	if (n->right != nullptr)
		visit(n->right);
}
void visit(node* n)
{
	display(n->d);
	if (n->left != nullptr)
		visit(n->left);
	if (n->right != nullptr)
		visit(n->right);
}
void visit(node* n)
{
	if (n->left != nullptr)
		visit(n->left);
	if (n->right != nullptr)
		visit(n->right);
	display(n->d);
}
(a)(b)(c)
Recursive binary tree traversal functions. Accessing a tree node is called visiting the node. Formally, computer scientists call visiting all the tree nodes in a particular order traversing the tree, and informally, they call it walking the tree. There are three tree traversal orders: inorder, preorder, and postorder. By writing visit as a recursive function, we can select a traversal order by where we locate the call to display. Notice that the traversal functions have two recursive paths (highlighted). The visit order for each traversal is as follows:
  1. DBEAFCG
  2. ABDECFG
  3. DEBFGCA
enum { INORDER, PREORDER, POSTORDER };

	...

void visit(node* n, int order)
{
	if (order == PREORDER)
		display(n->d);

	if (n->left != nullptr)
		visit(n->left, order);

	if (order == INORDER)
		display(n->d);

	if (n->right != nullptr)
		visit(n->right, order);

	if (order == POSTORDER)
		display(n->d);
}
One function, three traversal orders. The example demonstrates implementing all three traversal algorithms in a single function. It also demonstrates another authentic use for an enumeration.