Recursion is similar to induction proof, which mathematicians themselves admit is not obvious.
Therefore, if recursion is incomprehensible from the first time, you just need to consider it with a few examples. I would recommend examples that work with tree structures, as they are well visualized.
Imagine a binary tree. If the tree is small and consists of one node, then its height is equal to one. If this node has one or two children, then the height is two. How to write a program that calculates the height of the tree?
Recursion is perfect here. First, we set a general rule: the height of a binary tree is one plus the maximum of the heights of its child trees.
int getHeight(BinaryTree binaryTree) { int leftHeight = getHeight(binaryTree.left); int rightHeight = getHeight(binaryTree.right); return 1 + max(leftHeight, rightHeight); }
Such a function is not entirely correct, because it will never stop and will call itself until the stack overflows.
We can stop it if we get to the degenerate case. For example, we can assume the height of the empty subtree is zero, that is, getHeight(null) == 0 .
Then the function takes the form:
int getHeight(BinaryTree binaryTree) { if (binaryTree == null) return 0; int leftHeight = getHeight(binaryTree.left); int rightHeight = getHeight(binaryTree.right); return 1 + max(leftHeight, rightHeight); }
So we wrote a recursive calculation function. Here it is important to distinguish two parts of the recursive function that coincide with the proof by induction.
Part one: the general rule. Our algorithm should express the calculation of something through the same calculation, but for a smaller structure. Part two: stop. At the end of the computation there must be a degenerate case, which is considered not recursive, but trivial. Let's say the height of an empty tree is zero. The factorial unit is equal to one. And stuff like that.
Another example is arithmetic expressions. Take this:
g * h * i + k ^ l ^ m
Despite the fact that this expression does not look like a tree, in reality, we can present it in a tree form that will allow it to be calculated. This is what this tree looks like:
+ * ^ * ik ^ ghlm
In the tree nodes there are operators, in our case this is addition + , multiplication * and raising to a power ^ . Child elements are other expressions.
The function of calculating the value of the expression will look like this:
double calculate(Expression expression) { double left = calculate(expression.left); double right = calculate(expression.right); switch (expression.operator) { case '+': return left + right; case '*': return left * right; case '^': return pow(left, right); default: throw new Exception(); } }
Now we need to find degenerate cases to stop the recursive output. These are cases where the expression is a single variable g or i . The value of such an expression is the value of the variable itself. Suppose that for such variables we have in the expression object the field of operator is equal to 'v' and the value of such a variable is stored in the associative array variables . Then the function would look like this:
double calculate(Expression expression) { if (expression.operator == 'v') return variables[expression.name]; double left = calculate(expression.left); double right = calculate(expression.right); switch (expression.operator) { case '+': return left + right; case '*': return left * right; case '^': return pow(left, right); default: throw new Exception(); } }
This function is not very good, because in OOP-language one should use the polymorphic method instead of switch . But even in OOP language, polymorphic methods still recursively call each other.
For training, we can use a not very correct, but understandable option. Now try to solve the recursion problem yourself, for example, the well-known problem about the Hanoi tower. After a few such tasks, there will be no more recursion problems.