Chapter 6: Recursion
Subtopic Title: What Is Recursion?
Definition
- Recursion is a problem-solving process that breaks a problem into smaller versions of itself until a direct solution is found.
Causes
- Triggered when a problem can be reduced into smaller, identical problems.
- Used when iteration is complex or less intuitive.
Goals / Objectives
- To solve complex problems by simplifying them into smaller, manageable parts.
- To reach a base case that allows solving the original problem.
Importance
- Offers an alternative to iteration.
- Useful for problems with repetitive or nested structures.
- Makes complex solutions easier to understand and implement.
Procedures
- Break the problem into smaller versions.
- Solve the smallest version directly (base case).
- Use the solution of the smaller problem to solve the larger one.
- Continue until the original problem is solved.
Advantages & Disadvantages
Advantages:
- Simplifies complex problems.
- Can be more elegant and readable than iteration.
Disadvantages:
- May be less efficient due to repeated calls and memory usage.
- Requires careful design to avoid infinite recursion.
Impact / Effect
- Leads to a complete solution by solving smaller subproblems.
- Can increase memory usage due to stack frames.
- May slow down execution compared to iteration.
Examples
- Solving factorial using recursion.
- Towers of Hanoi problem.
- Recursive array processing.
Key Takeaways
- Recursion solves problems by breaking them into smaller versions.
- It needs a base case to stop the recursion.
- Useful when iteration is hard to implement.
- Can be less efficient than iteration due to memory and time overhead.
Subtopic Title: Terminology
Definition
- Recursion: The process of solving a problem by reducing it to smaller versions of itself.
- Recursive definition: A definition where something is defined in terms of a smaller version of itself.
- Stopping case: The case for which the solution is obtained directly.
- Recursive case: The case in a recursive algorithm where the problem is specified as a smaller version of the original problem.
- Recursive algorithm: An algorithm that solves a problem by reducing it to smaller versions of itself.
- Recursive method: A method that calls itself. Its body contains a statement that causes the same method to execute before completing the current call.
Causes
- Not specified in notes
Goals / Objectives
- To define and clarify key terms related to recursion.
- To distinguish between different components of recursive logic.
Importance
- Helps in understanding the structure and behavior of recursive solutions.
- Clarifies how recursion works and how it differs from iteration.
Procedures
- Use recursive definitions to build algorithms and methods.
- Identify stopping and recursive cases to structure recursive logic.
- Implement recursive methods that call themselves with modified parameters.
Advantages & Disadvantages
Advantages:
- Provides clarity in understanding recursive logic.
- Helps in designing correct recursive algorithms.
Disadvantages:
- Not specified in notes
Impact / Effect
- Enables accurate implementation and tracing of recursive methods.
- Supports better debugging and analysis of recursive behavior.
Examples
- Recursive method:
factorial(n)where the method calls itself withn-1. - Stopping case:
if (n == 0) return 1; - Recursive case:
return n * factorial(n - 1);
Key Takeaways
- Recursion involves breaking problems into smaller versions of themselves.
- A recursive method must include both a stopping case and a recursive case.
- Understanding terminology is essential for writing and analyzing recursive code.
- Recursive methods use the program stack to manage multiple calls.
Subtopic Title: Factorial (Recursive Definition)
Definition
- Factorial of a number
n(written asn!) is the product of all positive integers fromndown to 1. - Recursive definition:
0! = 1n! = n × (n - 1)!forn > 0
Causes
- Used when calculating permutations, combinations, or solving mathematical problems involving repeated multiplication.
Goals / Objectives
- To compute the factorial of a number using recursion.
- To demonstrate how recursion simplifies repetitive multiplication.
Importance
- Serves as a classic example of recursion.
- Helps learners understand recursive structure and base cases.
Procedures
- Recursive breakdown:
4! = 4 × 3!3! = 3 × 2!2! = 2 × 1!1! = 1 × 0!0! = 1(base case)
- Recursive method implementation:
public int factorial(int n) {
if (n == 0)
return 1; // Stopping case
else
return n * factorial(n - 1); // Recursive case
}Advantages & Disadvantages
Advantages:
- Simple and elegant for small values of
n. - Easy to understand and trace.
Disadvantages:
- Can be inefficient for large
ndue to deep recursion and stack usage. - Risk of stack overflow if base case is missing or unreachable.
Impact / Effect
- Demonstrates how recursion works through repeated method calls.
- Shows how the program stack handles recursive calls.
- Helps visualize recursion through box traces and stack frames.
Examples
factorial(4):4 × 3 × 2 × 1 = 24
- Box trace for
factorial(3):n = 3 → return 3 × factorial(2) n = 2 → return 2 × factorial(1) n = 1 → return 1 × factorial(0) n = 0 → return 1
Key Takeaways
- Factorial is a classic recursive problem with a clear base case.
- Recursive calls reduce the problem until the base case is reached.
- Each call waits for the result of the next before completing.
- Recursive methods use the program stack to manage calls.
Subtopic Title: Program Stack & Recursion Trace
Definition
- Program stack: A Last-In-First-Out (LIFO) memory structure used to manage method calls.
- Stack frame (also called activation record): A memory block that stores argument values, local variables, and return address for each method call.
- Recursion trace: A visual representation of recursive method execution, showing argument values, executed statements, and return values.
Causes
- Triggered automatically when a method is called, especially during recursion.
- Each recursive call creates a new stack frame.
Goals / Objectives
- To understand how recursive calls are managed in memory.
- To visualize the flow of recursive execution and return values.
Importance
- Essential for debugging and tracing recursive logic.
- Helps explain why recursion consumes more memory.
- Clarifies how control returns to previous calls after recursion completes.
Procedures
- Each method call pushes a new stack frame onto the program stack.
- The top frame is the active one (currently executing).
- When a method returns, its frame is popped off the stack.
- Recursion trace uses arrows:
- Down arrow (
↓) for new recursive calls. - Up arrow (
↑) for returning values.
- Down arrow (
Advantages & Disadvantages
Advantages:
- Makes recursive flow easier to visualize and understand.
- Supports step-by-step tracing of recursive logic.
Disadvantages:
- Can lead to stack overflow if recursion is too deep.
- Requires more memory than iteration due to multiple stack frames.
Impact / Effect
- Recursive methods use more memory due to repeated stack frame allocation.
- Execution slows down compared to iteration because of overhead in managing stack frames.
- Helps learners grasp recursion through box traces and visual flow.
Examples
- Box trace for
factorial(3):n = 3 → return 3 × factorial(2) n = 2 → return 2 × factorial(1) n = 1 → return 1 × factorial(0) n = 0 → return 1 - Box trace for
sumSeries(3):n = 3 → return sumSeries(2) + 0.33 n = 2 → return sumSeries(1) + 0.5 n = 1 → return 1
Key Takeaways
- Each recursive call creates a new stack frame.
- The program stack follows LIFO: last call is executed first.
- Recursion trace shows how values flow through recursive calls.
- Understanding stack behavior is key to mastering recursion.
Subtopic Title: Designing Recursive Methods & CountDown Implementations
Definition
- Recursive method design involves creating a method that solves a problem by calling itself with modified parameters until a base case is reached.
- CountDown is a recursive method that prints numbers in descending order from
nto1.
Causes
- Used when a task involves repetitive steps that reduce toward a base condition.
- Triggered by problems that naturally fit a recursive structure.
Goals / Objectives
- To design recursive methods that are correct, efficient, and easy to understand.
- To explore different ways of implementing recursion with the same output.
Importance
- Shows how recursion can be structured in multiple ways.
- Highlights the role of stopping cases and recursive calls in method design.
- Demonstrates how small changes in logic affect recursion depth and behavior.
Procedures
Steps for Designing Recursive Methods
- List all stopping (base) cases
- List recursive cases
- Ensure each recursive case moves toward a stopping case
- Arrange cases in correct sequence
General Structures of Recursive Methods
-
Structure A: Stopping and recursive cases have different actions
if (stopping case) solve directly; else recursively solve smaller version; -
Structure B: Common action in both cases
perform common step; if (recursive case) recursively solve smaller version; -
Structure C: No action in stopping case
if (recursive case) recursively solve smaller version;
CountDown Implementations
Implementation 1
public void countDown(int n) {
if (n == 1)
System.out.println(n);
else {
System.out.println(n);
countDown(n - 1);
}
}- Stopping case is checked first.
printlnappears in both cases (redundant).
Implementation 2
public void countDown(int n) {
if (n >= 1) {
System.out.println(n);
countDown(n - 1);
}
}- Removes redundant
println. - Recursive case is checked.
- When
n = 1, it callscountDown(0)(stopping case with no action).
Implementation 3
public void countDown(int n) {
System.out.println(n);
if (n > 1)
countDown(n - 1);
}- Displays
nfirst, then checks recursive case. - Uses
n > 1instead ofn >= 1, resulting in one less recursive call.
Advantages & Disadvantages
Advantages:
- Flexible design options for different use cases.
- Helps understand recursion flow and stopping logic.
Disadvantages:
- Small changes can affect recursion depth.
- Risk of unnecessary calls or stack overflow if not carefully designed.
Impact / Effect
- Different implementations affect how many recursive calls are made.
- Efficient design reduces redundancy and improves clarity.
- Helps students understand recursion through visual traces.
Examples
Box Trace for countDown(3)
countDown(3)
→ display 3
→ countDown(2)
→ display 2
→ countDown(1)
→ display 1
Key Takeaways
- Recursive methods need clear stopping and recursive cases.
- CountDown shows how logic placement affects recursion depth.
- Multiple implementations can achieve the same output with different efficiency.
- Always ensure recursive calls move toward the base case.
Subtopic Title: Recursive Series & Combinations
Part A: Recursive Series – sumSeries(n)
Definition
- sumSeries(n) computes the sum of the series:
1 + 1/2 + 1/3 + ... + 1/n
wherenis a positive integer.
Causes
- Used when calculating harmonic series or approximations in mathematics.
Goals / Objectives
- To compute the sum of a decreasing fractional series using recursion.
Importance
- Demonstrates recursion with non-integer operations.
- Helps understand recursive accumulation of values.
Procedures
- Recursive method:
public double sumSeries(int n) {
if (n <= 0)
return 0.0;
else if (n == 1)
return 1;
else
return sumSeries(n - 1) + 1.0 / n;
}- Each call adds
1/nto the result ofsumSeries(n - 1).
Advantages & Disadvantages
Advantages:
- Simple and readable recursive structure.
- Handles fractional values effectively.
Disadvantages:
- Less efficient than iteration for large
n. - Floating-point precision may affect accuracy.
Impact / Effect
- Accumulates fractional values recursively.
- Shows how recursion can be used beyond integers.
Examples
Box Trace for sumSeries(3)
n = 3 → return sumSeries(2) + 0.33
n = 2 → return sumSeries(1) + 0.5
n = 1 → return 1
Final result: 1.83
Key Takeaways
- Recursive series can handle fractional values.
- Each call builds on the result of the previous one.
- Base case ensures recursion stops at
n = 1. - Useful for understanding recursive accumulation.
Part B: Combinations – C(n, k)
Definition
- C(n, k) computes the number of ways to choose
kobjects fromnobjects. - Recursive definition:
C(n, k) = 0ifk > nC(n, k) = 1ifk = 0ork = nC(n, k) = C(n - 1, k - 1) + C(n - 1, k)if0 < k < n
Causes
- Used in combinatorics and probability calculations.
Goals / Objectives
- To calculate combinations recursively using mathematical properties.
Importance
- Shows how recursion can solve mathematical problems with multiple base cases.
- Useful in statistics, probability, and algorithm design.
Procedures
- Recursive method:
public int c(int n, int k) {
if (k == 0 || k == n)
return 1;
else if (k > n)
return 0;
else
return c(n - 1, k - 1) + c(n - 1, k);
}- Combines results from two smaller subproblems.
Advantages & Disadvantages
Advantages:
- Elegant recursive structure.
- Handles multiple base cases.
Disadvantages:
- Can be inefficient for large
nandkdue to repeated calls. - May require memoization for optimization.
Impact / Effect
- Builds combination values from smaller subproblems.
- Demonstrates recursion with branching logic.
Examples
C(4, 2)would compute:C(3, 1) + C(3, 2)- Each of those would further break down recursively.
Key Takeaways
- Recursive combinations use multiple base cases.
- Each call splits into two smaller problems.
- Useful for understanding branching recursion.
- Efficiency can be improved with optimization techniques.
Subtopic Title: Recursively Processing Arrays & Linked Chains
Part A: Recursively Processing Arrays
Definition
- Recursive array processing involves handling array elements by breaking the array into smaller parts and applying recursion to each part.
Causes
- Used when array elements need to be processed in a specific order (e.g., first-to-last, last-to-first, or by halves).
- Triggered by problems that benefit from divide-and-conquer strategies.
Goals / Objectives
- To process array elements recursively using different strategies.
- To demonstrate how recursion can traverse arrays without loops.
Importance
- Shows how recursion can replace iteration for array traversal.
- Helps understand recursive logic in data structures.
Procedures
Three Recursive Strategies:
-
displayArray1()
- Displays the first element, then recursively displays the rest.
-
displayArray2()
- Recursively displays all preceding elements, then displays the last element.
-
displayArray3()
- Divides the array into two halves and recursively displays each half.
These methods are typically private and used internally in ADTs (Abstract Data Types).
Advantages & Disadvantages
Advantages:
- Offers flexible traversal strategies.
- Can simplify complex array operations.
Disadvantages:
- May be less efficient than iteration.
- Requires careful handling of array indices and base cases.
Impact / Effect
- Enables recursive traversal of arrays in different directions.
- Supports modular design in ADT implementations.
- Can increase memory usage due to recursive calls.
Examples
- displayArray1():
0 → 1 → 2 → 3 → 4 → 5 → 6 - displayArray2():
6 ← 5 ← 4 ← 3 ← 2 ← 1 ← 0 - displayArray3(): Divides array into left and right halves and displays recursively.
Key Takeaways
- Arrays can be processed recursively using different strategies.
- Recursive methods can replace loops for traversal.
- Each method has a unique order of processing.
- Useful for understanding divide-and-conquer logic.
Part B: Recursively Processing a Linked Chain
Definition
- Recursive linked chain processing involves traversing a chain of linked nodes using recursion.
Causes
- Used when working with linked lists or node-based structures.
- Triggered by the need to process each node in sequence.
Goals / Objectives
- To process each node in a linked chain recursively.
- To demonstrate recursion in dynamic data structures.
Importance
- Highlights recursion in non-array structures.
- Essential for understanding linked list operations.
Procedures
- Use a reference to the first node as the method parameter.
- Process the current node.
- Recursively process the rest of the chain.
Example:
toString()method inSimpleList.javauses recursion to build a string representation of the list.
Advantages & Disadvantages
Advantages:
- Simplifies traversal logic.
- Avoids manual loop constructs.
Disadvantages:
- May be harder to trace and debug.
- Can lead to stack overflow in long chains.
Impact / Effect
- Enables clean traversal of linked structures.
- Supports recursive design in list-based ADTs.
Examples
- Processing nodes:
Node1 → Node2 → Node3 → ... → NodeN
Key Takeaways
- Linked chains can be processed recursively node by node.
- Recursion simplifies traversal logic in dynamic structures.
- Each node is handled in its own recursive call.
- Useful for implementing methods like
toString()in linked lists.
Subtopic Title: Time Efficiency, Towers of Hanoi & Fibonacci Analysis
Part A: Time Efficiency of Recursive Methods
Definition
- Time efficiency refers to how fast a recursive method executes, often measured using Big-O notation.
Causes
- Influenced by the number of recursive calls and how much work each call performs.
- Depends on whether recursion is linear, exponential, or optimized.
Goals / Objectives
- To analyze how efficient a recursive method is compared to its iterative counterpart.
- To understand the performance impact of recursion.
Importance
- Helps determine whether recursion is suitable for a given problem.
- Critical for performance-sensitive applications.
Procedures
- Example:
countDown(n)has time efficiency ofO(n)because it makesnrecursive calls.
Advantages & Disadvantages
Advantages:
- Some recursive methods are efficient and easy to implement.
Disadvantages:
- Recursive methods often use more memory and time than iteration.
- Poorly designed recursion can lead to exponential time complexity.
Impact / Effect
- Recursive methods may execute slower due to overhead from stack frames.
- Efficiency affects usability in real-world systems.
Examples
countDown(n)→O(n)time complexity.Fibonacci(n)→O(2^n)time complexity (inefficient).
Key Takeaways
- Efficiency depends on recursion depth and structure.
- Linear recursion is manageable; exponential recursion is costly.
- Always analyze time complexity before choosing recursion.
Part B: Towers of Hanoi
Definition
- Towers of Hanoi is a puzzle involving moving disks between three pegs following specific rules.
Causes
- Designed as a recursive problem due to its repetitive structure.
Goals / Objectives
- To move all disks from the start peg to the end peg using a temporary peg.
- To follow the rules without placing larger disks on smaller ones.
Importance
- Classic example of recursion.
- Demonstrates divide-and-conquer strategy.
Procedures
Rules:
- Move one disk at a time (must be topmost).
- No disk may rest on a smaller disk.
- Temporary storage on the second peg is allowed.
Recursive Algorithm:
solveTowers(numberOfDisks, startPole, tempPole, endPole)
if (numberOfDisks == 1)
Move disk from startPole to endPole
else {
solveTowers(numberOfDisks - 1, startPole, endPole, tempPole)
Move disk from startPole to endPole
solveTowers(numberOfDisks - 1, tempPole, startPole, endPole)
}Advantages & Disadvantages
Advantages:
- Recursive solution is simple and elegant.
- Avoids complex nested loops.
Disadvantages:
- Inefficient for large number of disks.
- Requires many recursive calls.
Impact / Effect
- Solves a complex problem using simple recursive steps.
- Shows how recursion can simplify logic.
Examples
- Moving 3 disks:
- Move disk 1 from A to C
- Move disk 2 from A to B
- Move disk 1 from C to B
- Continue until all disks are moved to C
Key Takeaways
- Towers of Hanoi is a perfect fit for recursion.
- Recursive algorithm breaks problem into smaller disk moves.
- Each move follows strict rules.
- Demonstrates power of recursion in problem-solving.
Part C: Fibonacci Analysis
Definition
- Fibonacci sequence: A series where each number is the sum of the two preceding ones.
- Starts with
1, 1, 2, 3, 5, 8, 13...
- Starts with
Causes
- Used in mathematical modeling, nature patterns, and algorithm design.
Goals / Objectives
- To compute Fibonacci numbers using recursion.
- To analyze why the naive recursive solution is inefficient.
Importance
- Highlights the downside of recursion when overlapping subproblems exist.
- Encourages optimization techniques like memoization.
Procedures
Recursive Algorithm:
Fibonacci(n)
if (n <= 1)
return 1
else
return Fibonacci(n - 1) + Fibonacci(n - 2)Call Analysis:
Fibonacci(6)results in:F2computed 5 timesF3computed 3 timesF4computed 2 timesF5andF6computed once
Efficiency:
- Time complexity:
O(2^n)(exponential)
Iterative Alternative:
- Uses loop to compute values in
O(n)time.
Advantages & Disadvantages
Advantages:
- Recursive version is easy to write and understand.
Disadvantages:
- Extremely inefficient due to repeated calculations.
- Exponential growth in number of calls.
Impact / Effect
- Recursive Fibonacci is a poor solution for large
n. - Demonstrates need for optimization in recursive algorithms.
Examples
- Recursive computation of
F6involves 41 calls. - Iterative computation of
F6uses 6 steps.
Key Takeaways
- Naive recursion can be inefficient.
- Fibonacci is a classic case for memoization or iteration.
- Always analyze call patterns before using recursion.
- Iterative version is significantly faster and uses less memory.
Subtopic Title: Tail Recursion & Mutual Recursion
Part A: Tail Recursion
Definition
- Tail recursion occurs when the last action in a recursive method is the recursive call itself.
Causes
- Happens naturally when recursion is used for straightforward repetition.
- Triggered by problems that don’t require post-processing after the recursive call.
Goals / Objectives
- To simplify recursion so it can be converted into iteration.
- To reduce overhead by making recursion more efficient.
Importance
- Tail-recursive methods are easier to optimize.
- Can be rewritten as loops to improve performance.
Procedures
Tail Recursive Method:
public static void countDown(int n) {
if (n >= 1) {
System.out.println(n);
countDown(n - 1);
}
}Converted to Iteration:
public static void countDown(int n) {
while (n >= 1) {
System.out.println(n);
n--;
}
}Advantages & Disadvantages
Advantages:
- Easier to convert to iteration.
- Reduces memory usage compared to regular recursion.
Disadvantages:
- Still uses recursion unless explicitly converted.
- May not be supported by all compilers for optimization.
Impact / Effect
- Improves efficiency when converted to loops.
- Reduces stack frame usage.
Examples
countDown(n)is a tail-recursive method.- Iterative version performs the same task with better efficiency.
Key Takeaways
- Tail recursion ends with the recursive call.
- Can be converted to iteration for better performance.
- Useful for repetitive tasks like countdowns.
- Helps reduce memory overhead.
Part B: Mutual Recursion
Definition
- Mutual recursion (also called indirect recursion) occurs when two or more methods call each other in a cycle.
Causes
- Arises in problems with alternating logic or layered parsing.
- Triggered when one method’s logic depends on another’s result.
Goals / Objectives
- To handle problems with interdependent conditions.
- To demonstrate recursion beyond single-method calls.
Importance
- Shows complexity of recursive relationships.
- Useful in parsing expressions and grammar rules.
Procedures
- Method A calls Method B
- Method B calls Method A
- Cycle continues until a stopping condition is met
Advantages & Disadvantages
Advantages:
- Can model complex interdependent logic.
- Useful in compiler design and expression parsing.
Disadvantages:
- Difficult to trace and debug.
- Can lead to infinite recursion if not carefully designed.
Impact / Effect
- Adds complexity to recursion flow.
- Requires careful design to avoid logical errors.
Examples
- Parsing expressions:
isExpression()→isTerm()→isFactor()→isExpression()(cycle)
Key Takeaways
- Mutual recursion involves multiple methods calling each other.
- Adds complexity and requires careful stopping logic.
- Useful in specific domains like grammar parsing.
- Harder to trace than single-method recursion.
Subtopic Title: Iteration vs Recursion & Final Discussion
Definition
- Iteration uses loops to repeat actions.
- Recursion uses method calls to repeat actions by reducing the problem.
Causes
- Choice depends on problem structure and efficiency needs.
Goals / Objectives
- To compare recursion and iteration.
- To guide decision-making based on problem nature.
Importance
- Helps choose the right approach for solving problems.
- Critical for performance and clarity in programming.
Procedures
- Evaluate the problem:
- Is it naturally recursive?
- Is efficiency critical?
- Choose recursion for repetitive, nested, or divide-and-conquer problems.
- Choose iteration for straightforward repetition.
Advantages & Disadvantages
Recursion Advantages:
- Elegant for nested or repetitive structures.
- Easier to implement for some problems.
Recursion Disadvantages:
- Slower due to memory and call overhead.
- Harder to debug.
Iteration Advantages:
- Faster and more memory-efficient.
- Easier to trace and debug.
Iteration Disadvantages:
- Can be harder to write for complex problems.
Impact / Effect
- Affects performance, readability, and maintainability.
- Influences memory usage and execution time.
Examples
- Fibonacci: Iterative version is
O(n), recursive isO(2^n) - CountDown: Both recursive and iterative versions work, but iteration is more efficient.
Key Takeaways
- Use iteration when it’s simpler and more efficient.
- Use recursion when the problem is naturally recursive.
- Efficiency matters in performance-critical systems.
- Today’s computers can handle recursion, but choose wisely.