The concept of recursion is fundamental in mathematics and computer science. The simple definition is that a recursive program in a coding language is one that invokes itself (just as a recursive function in mathematics is one that is defined in terms of itself). A recursive program cannot invoke itself always, or it would never stop (just as a recursive function cannot be defined in terms of itself always, or the definition would be circular); so a second essential ingredient is that there must be a termination condition when the program can cease to invoke itself (and when the mathematical function is not defined in terms of itself). All practical computations can be couched in a recursive framework.
The study of recursion is intertwined with the study of recursively defined structures known as trees. We use trees both to help us understand and analyze recursive programs and as explicit data structures. We have already encountered an app of trees (although not a recursive one) in . The connection between recursive programs and trees underlies a great deal of the material in this tutorial. We use trees to understand recursive programs; we use recursive programs to build trees; and we draw on the fundamental relationship between both (and recurrence relations) to analyze algorithms. Recursion helps us to develop elegant and efficient data structures and algorithms for all manner of apps.
Our primary purpose in this chapter is to examine recursive programs and data structures as practical tools. First, we discuss the relationship between mathematical recurrences and simple recursive programs, and we consider a number of examples of practical recursive programs. Next, we examine the fundamental recursive scheme known as divide and conquer, which we use to solve fundamental problems in several later sections of this tutorial. Then, we consider a general approach to implementing recursive programs known as dynamic programming, which provides effective and elegant solutions to a wide class of problems. Next, we consider trees, their mathematical properties, and associated algorithms in detail, including basic methods for tree traversal that underlie recursive tree-processing programs. Finally, we consider closely related algorithms for processing graphs—we look specifically at a fundamental recursive program, depth-first search, that serves as the basis for many graph-processing algorithms.
As we shall see, many interesting algorithms are simply expressed in Java with recursive methods, and many algorithm designers prefer to express computations recursively. We also investigate nonrecursive alternatives in detail. Not only can we often devise simple stack-based algorithms that are essentially equivalent to recursive algorithms, but also we can often find nonrecursive alternatives that achieve the same final result through a different sequence of computations. The recursive formulation provides a structure within which we can seek more efficient alternatives.
A full discussion of recursion and trees could fill an entire tutorial, for they arise in many apps throughout computer science and are pervasive outside of computer science as well. Indeed, it might be said that this tutorial is filled with a discussion of recursion and trees, for they are present, in a fundamental way, in every one of the tutorial's chapters.