3. Topic 3(COLON) Data Structures and Programming Paradigms

Lesson 3.7: Recursion And Abstract Data Types

#### Lesson focus #### Learning outcomes Students should be able to:.

Lesson 3.7: Recursion and Abstract Data Types

Introduction

Welcome to our lesson on recursion and abstract data types! πŸŽ‰ In this lesson, we'll dive into two important topics in computer science that will help expand your programming skills.

Learning Outcomes

By the end of this lesson, you should be able to:

  • Understand the concept of recursion and how to identify the base case and the recursive case.
  • Trace the execution of recursive functions using examples like factorial and countdown.
  • Compare recursive and iterative solutions for solving the same problem.
  • Gain knowledge about abstract data types, specifically stacks (LIFO) and queues (FIFO), and their applications.
  • Write a simple recursive function with a correct base case and trace its execution.

Understanding Recursion

Recursion is a powerful programming technique in which a function calls itself to solve smaller instances of the same problem. This is often done through two key concepts: the base case and the recursive case.

Base Case and Recursive Case

  • Base Case: This is the condition under which the recursion stops. It prevents the function from calling itself indefinitely.
  • Recursive Case: This part of the function contains the self-referential call that reduces the problem size with each call.

Example: Factorial Calculation

The factorial of a number $n$, denoted as $n!$, is defined as:

  • $n! = n \times (n - 1)!$ for $n > 0$
  • $0! = 1$ (base case)

Here's how a recursive function for factorial looks in Python:

def factorial(n):
    if n == 0:
        return 1  # base case
    else:
        return n * factorial(n - 1)  # recursive case

In this code, if you call factorial(5), it will calculate:

  • $5 \times factorial(4)$
  • $4 \times factorial(3)$
  • $3 \times factorial(2)$
  • $2 \times factorial(1)$
  • $1 \times factorial(0)$
  • finally returns $1$

Thus, factorial(5) equals $120$! πŸŽ‰

Example: Countdown

Another common example of recursion is a countdown function, which prints numbers from a given number down to zero. Here’s an implementation:

def countdown(n):
    if n < 0:
        return  # base case
    print(n)  # processing step
    countdown(n - 1)  # recursive call

Calling countdown(5) will output:

5
4
3
2
1
0

Tracing the Call Stack

When tracing recursive functions, you'll need to visualize the call stack β€” a structure that keeps track of active functions. Let's take the countdown example:

  • Calling countdown(3) pushes an activation record onto the stack.
  • The function calls itself, each time reducing the parameter until it hits the base case.
  • Once the base case is reached, it unwinds back through the calls, returning to print each number.

Recursive vs. Iterative Solutions

Now, let's examine how recursion compares with iteration (using loops). An iterative version of the factorial function can be written as:

def factorial_iterative(n):
    result = 1
    for i in range(1, n + 1):
        result *= i
    return result
  • For many problems, a recursive solution can be elegant and easier to understand. However, it may lead to higher memory usage due to maintaining multiple function calls in the call stack.
  • Iterative solutions, although sometimes longer, are generally more efficient in terms of memory since they do not require additional stack frames.

Comparing Factorial Calculations

Both the recursive and iterative approaches to factorial yield the same results, but let's consider performance:

  • Recursive: More readable, but a large input can cause stack overflow due to depth exceeding limits.
  • Iterative: More efficient for larger inputs and less risk of stack overflow.

Abstract Data Types (ADTs)

Now that we've explored recursion, let’s move onto another core concept: Abstract Data Types (ADTs). An ADT is a data structure that is defined by its behavior (operations) rather than its implementation. Two common ADTs you'll encounter are the stack and the queue.

Stack (LIFO)

A stack is a collection of elements that follows the Last In, First Out (LIFO) principle. The most recent item added is the first one to be removed. Think of it like a stack of plates β€” you add and remove from the top!

  • Common Operations:
  • push(): Add an element to the stack.
  • pop(): Remove the top element from the stack.

Queue (FIFO)

A queue, conversely, follows the First In, First Out (FIFO) principle. This means that the first item added to the queue will be the first one to be removed β€” just like waiting in line at a bank! πŸ˜„

  • Common Operations:
  • enqueue(): Add an element to the back of the queue.
  • dequeue(): Remove the front element from the queue.

Usage of Stacks and Queues

  • Stacks: Often used in undo features, expression evaluation, and backtracking algorithms.
  • Queues: Common in scheduling tasks, handling requests, and breadth-first search methodologies.

Conclusion

Recursion and abstract data types are foundational concepts in programming that enable us to efficiently manage data and solve problems. Understanding when and how to apply these techniques is crucial as you progress in your computing studies.

Study Notes

  • Recursion involves functions calling themselves, with a clearly defined base and recursive case.
  • Common examples include factorial and countdown functions.
  • Tracing the call stack helps visualize how recursive calls unfold and return.
  • Recursive methods can be compared to iterative methods for performance and efficiency.
  • Abstract data types, like stacks (LIFO) and queues (FIFO), define data structures through their behavior, guiding their usage in algorithms.

Practice Quiz

5 questions to test your understanding

Lesson 3.7: Recursion And Abstract Data Types β€” Computing | A-Warded