Recursion Problem Solving - I
Recursive problem solving begins by defining exactly what a function should return. The current problem is then expressed in terms of the same function applied to a smaller input.
The problems in this editorial focus on numbers and their digits. Each solution follows the same broad process:
Define the responsibility of the recursive function.
Identify how to reduce the input.
Determine the contribution of the current step.
Choose a correct base case and return value.
Combine the current contribution with the smaller problem's answer.
Problem 1: Calculate the Power of a Number
Given a base b and a non-negative exponent x, calculate: bx
For example, if b = 5 and x = 3:
53 = 5 * 5 * 5 = 125
Iterative Approach
The iterative solution begins with an accumulator equal to 1. It multiplies the accumulator by the base exactly x times.
Initially,
answer = 1.After the first multiplication,
answer = 5.After the second multiplication,
answer = 25.After the third multiplication,
answer = 125.
Recursive Definition
Define power(b, x) as the value of bx. The exponent can be reduced by one:
power(b, x) = b * power(b, x - 1)
The smallest exponent is zero:
power(b, 0) = 1
Any nonzero base raised to the power zero is one. In ordinary integer power implementations, 00 is also commonly returned as 1 by convention.
#include <iostream>using namespace std;long long powerIterative(int base, int exponent) { long long answer = 1; for (int count = 1; count <= exponent; count++) { answer *= base; } return answer;}long long powerRecursive(int base, int exponent) { if (exponent == 0) { return 1; } return base * powerRecursive(base, exponent - 1);}int main() { int base = 5; int exponent = 3; cout << powerIterative(base, exponent) << '\n'; cout << powerRecursive(base, exponent) << '\n'; return 0;}Dry Run for 5 to the Power 3
The calls expand as follows:
powerRecursive(5, 3) = 5 * powerRecursive(5, 2)powerRecursive(5, 2) = 5 * powerRecursive(5, 1)powerRecursive(5, 1) = 5 * powerRecursive(5, 0)powerRecursive(5, 0) = 1
The answers then return through the call stack:
powerRecursive(5, 0)returns1.powerRecursive(5, 1)returns5 * 1 = 5.powerRecursive(5, 2)returns5 * 5 = 25.powerRecursive(5, 3)returns5 * 25 = 125.
Complexity Analysis
Time complexity:
O(x).Recursive stack space:
O(x).
This direct recursive method assumes that the exponent is non-negative. Negative exponents require a fractional return type and a separate definition.
Problem 2: Count the Digits in a Number
Given a non-negative integer n, determine how many decimal digits it contains.
For example: n = 498
The number contains three digits: 4, 9, and 8.
Removing the Last Digit
Integer division by 10 removes the final decimal digit:
498 / 10 = 4949 / 10 = 44 / 10 = 0
Each reduction removes exactly one digit. Therefore, the recursive function contributes one and asks the smaller number for its remaining digit count:
countDigits(n) = 1 + countDigits(n / 10)
The helper function stops when n reaches zero:
countDigits(0) = 0
This zero represents an empty remaining suffix during recursion. The original number zero is a special input because its decimal representation contains one digit.
#include <iostream>using namespace std;int countDigitsIterative(int n) { if (n == 0) { return 1; } int count = 0; while (n > 0) { count++; n /= 10; } return count;}int countDigitsRecursiveHelper(int n) { if (n == 0) { return 0; } return 1 + countDigitsRecursiveHelper(n / 10);}int countDigitsRecursive(int n) { return n == 0 ? 1 : countDigitsRecursiveHelper(n);}int main() { int n = 498; cout << countDigitsIterative(n) << '\n'; cout << countDigitsRecursive(n) << '\n';}Iterative Dry Run for 498
Start with
n = 498andcount = 0.Increment the count to 1 and reduce
nto 49.Increment the count to 2 and reduce
nto 4.Increment the count to 3 and reduce
nto 0.The loop ends and returns 3.
Recursive Dry Run for 498
The calls expand as follows:
countDigits(498) = 1 + countDigits(49)countDigits(49) = 1 + countDigits(4)countDigits(4) = 1 + countDigits(0)countDigits(0) = 0
The returned values are:
countDigits(4) = 1 + 0 = 1countDigits(49) = 1 + 1 = 2countDigits(498) = 1 + 2 = 3
Complexity Analysis
If the number contains d digits:
Time complexity:
O(d), equivalent toO(log10 n)for positiven.Recursive stack space:
O(d).
Problem 3: Calculate the Sum of Digits
Given a non-negative integer, calculate the sum of its decimal digits.
For example:
115 = 1 + 1 + 5 = 7
Extracting the Last Digit
Two arithmetic operations are central to digit problems:
n % 10extracts the last digit.n / 10removes the last digit.
For 115:
115 % 10 = 5and115 / 10 = 11.11 % 10 = 1and11 / 10 = 1.1 % 10 = 1and1 / 10 = 0.
Recursive Definition
The current call contributes the final digit and recursively calculates the sum of the remaining prefix:
sumDigits(n) = (n % 10) + sumDigits(n / 10)
The base case is: sumDigits(0) = 0
Zero is the correct identity because adding zero does not change the accumulated sum.
#include <iostream>using namespace std;int sumDigitsIterative(int n) { int sum = 0; while (n > 0) { sum += n % 10; n /= 10; } return sum;}int sumDigitsRecursive(int n) { if (n == 0) { return 0; } int lastDigit = n % 10; int remainingSum = sumDigitsRecursive(n / 10); return lastDigit + remainingSum;}int main() { int n = 115; cout << sumDigitsIterative(n) << '\n'; cout << sumDigitsRecursive(n) << '\n';}Iterative Dry Run for 115
Start with
sum = 0.Extract 5, making
sum = 5; reduce the number to 11.Extract 1, making
sum = 6; reduce the number to 1.Extract 1, making
sum = 7; reduce the number to 0.Return 7.
Recursive Dry Run for 115
The calls expand into:
sumDigits(115) = 5 + sumDigits(11)sumDigits(11) = 1 + sumDigits(1)sumDigits(1) = 1 + sumDigits(0)sumDigits(0) = 0
The answers return as follows:
sumDigits(1) = 1 + 0 = 1sumDigits(11) = 1 + 1 = 2sumDigits(115) = 5 + 2 = 7
Why Digit Order Does Not Matter Here
Addition is commutative. The digits may effectively be combined as 1 + 1 + 5 or 5 + 1 + 1; both orders produce 7. This allows each call to extract the final digit and combine it with the recursively calculated sum of the remaining number.
Complexity Analysis
Time complexity:
O(d).Recursive stack space:
O(d).
Problem 4: Calculate the Product of Digits
Given a non-negative integer, calculate the product of its digits.
For example: 125 = 1 * 2 * 5 = 10
Recursive Definition
The current call extracts the final digit and multiplies it by the product returned for the remaining number:
productDigits(n) = (n % 10) * productDigits(n / 10)
The recursive helper uses the base case:
productDigits(0) = 1
The base case returns one because one is the multiplicative identity: x * 1 = x
Returning zero would cause every result above the base case to become zero, regardless of the actual digits.
#include <iostream>using namespace std;int productDigitsIterative(int n) { if (n == 0) { return 0; } int product = 1; while (n > 0) { product *= n % 10; n /= 10; } return product;}int productDigitsRecursiveHelper(int n) { if (n == 0) { return 1; } return (n % 10) * productDigitsRecursiveHelper(n / 10);}int productDigitsRecursive(int n) { return n == 0 ? 0 : productDigitsRecursiveHelper(n);}int main() { int n = 125; cout << productDigitsIterative(n) << '\n'; cout << productDigitsRecursive(n) << '\n';}Recursive Dry Run for 125
The calls expand as follows:
productDigits(125) = 5 * productDigits(12)productDigits(12) = 2 * productDigits(1)productDigits(1) = 1 * productDigits(0)The helper's
productDigits(0)returns1.
The answers return through the stack:
productDigits(1) = 1 * 1 = 1productDigits(12) = 2 * 1 = 2productDigits(125) = 5 * 2 = 10
The Input Zero and the Empty Suffix
There is an important distinction between two meanings of zero:
When zero appears after repeatedly removing digits, it represents an empty remaining suffix. Its product identity must be one.
When the original input is the number zero, its only digit is zero, so the product of its digits is zero.
The public function handles the original input zero separately, while the recursive helper returns one at the end of a positive number's decomposition.
Numbers Containing a Zero Digit
If the number is 105, one extracted digit is zero. The multiplication correctly becomes zero:
1 * 0 * 5 = 0
Why Digit Order Does Not Matter Here
Multiplication is commutative. The product remains the same whether the digits are processed as 1 * 2 * 5 or 5 * 2 * 1.
Complexity Analysis
Time complexity:
O(d).Recursive stack space:
O(d).
Problem 5: Reverse a Number
Given a non-negative integer, return the number formed by reversing its decimal digits.
For example: 456 becomes 654.
Iterative Construction
The reverse is built one digit at a time using:
reversed = reversed * 10 + lastDigit
Multiplication by 10 shifts the existing digits one place to the left. The extracted digit is then placed in the newly opened units position.
For 456:
Start with
reversed = 0.Append 6:
0 * 10 + 6 = 6.Append 5:
6 * 10 + 5 = 65.Append 4:
65 * 10 + 4 = 654.
Why an Accumulator Is Required
Unlike addition and multiplication, decimal digit stitching is order-sensitive. The digits must be appended in the order in which they are extracted: 6, then 5, then 4.
Every recursive call needs the reverse constructed by the previous calls. Therefore, the partially built number is carried as an accumulator parameter.
#include <iostream>using namespace std;int reverseIterative(int n) { int reversed = 0; while (n > 0) { reversed = reversed * 10 + n % 10; n /= 10; } return reversed;}int reverseRecursiveHelper(int n, int reversed) { if (n == 0) { return reversed; } int updated = reversed * 10 + n % 10; return reverseRecursiveHelper(n / 10, updated);}int reverseRecursive(int n) { return reverseRecursiveHelper(n, 0);}int main() { int n = 456; cout << reverseIterative(n) << '\n'; cout << reverseRecursive(n) << '\n';}Recursive Dry Run for 456
reverseHelper(456, 0)creates6and callsreverseHelper(45, 6).reverseHelper(45, 6)creates65and callsreverseHelper(4, 65).reverseHelper(4, 65)creates654and callsreverseHelper(0, 654).reverseHelper(0, 654)reaches the base case and returns654.
The value 654 is returned by every suspended call until it reaches main().
Trailing Zeros in the Original Number
Integer values do not preserve leading zeros. Reversing 1200 mathematically produces the digit sequence 0021, but as an integer the result is simply 21.
Complexity Analysis
Time complexity:
O(d).Recursive stack space:
O(d).
Comparing the Digit Recurrences
Several digit problems reduce the number using n / 10, but they combine the current digit differently.
Digit count:
1 + countDigits(n / 10)Digit sum:
(n % 10) + sumDigits(n / 10)Digit product:
(n % 10) * productDigits(n / 10)Number reversal: carry
reversed * 10 + n % 10into the next call.
Counting, addition, and multiplication can combine independently returned values because their relevant operations do not depend on digit order. Reversal must preserve construction order, so it carries the partially built answer forward.
Choosing the Correct Base-Case Value
The value returned by a base case participates in every result above it. It must be neutral for the operation being performed.
Power uses multiplication, so exponent zero returns
1.Digit counting uses addition, so the empty suffix returns
0.Digit sum uses addition, so the empty suffix returns
0.Digit product uses multiplication, so the empty suffix returns
1.Number reversal returns the accumulator already constructed.
A mechanically chosen base value can ruin the entire recurrence. For example, returning zero from the product helper would force every product to become zero.
Common Mistakes
Forgetting to reduce the exponent in the power function.
Using a base case that cannot be reached.
Returning zero from a multiplicative recurrence.
Treating the helper's empty suffix as identical to the original input zero.
Using
n / 10when the final digit is needed andn % 10when the remaining number is needed.Forgetting to return the result of the recursive call.
Attempting to reverse a number without carrying the partially constructed reverse.
Ignoring possible integer overflow for large powers, products, or reversed values.
Applying these positive-number recurrences to negative inputs without first defining the expected sign behavior.
Problem-Solving Pattern
For each recursive numerical problem, use the following process:
Write down what
function(n)means.Find a smaller input, often
n - 1orn / 10.Determine the contribution of the current call.
Write the mathematical recurrence.
Identify the smallest meaningful input.
Choose a base return value compatible with the operation.
Dry-run the downward calls.
Dry-run the returned values in reverse order.
Check special inputs such as zero.
Key Takeaways
A recursive function should have a precise responsibility.
The recursive call must receive a smaller version of the problem.
Exponentiation reduces the exponent by one.
Digit problems reduce the number using integer division by 10.
The modulo operator extracts the current last digit.
Additive recurrences naturally use zero as their base identity.
Multiplicative recurrences naturally use one as their base identity.
Order-independent operations can combine current values with recursively returned answers.
Order-dependent construction, such as number reversal, often requires an accumulator.
A complete dry run must follow both call creation and returned values.
Be the first to add a comment.