Lesson 19 • Advanced

Algorithm Design Patterns

35 minutes
Problem Solving

What are Algorithm Design Patterns?

Algorithm design patterns are general, reusable approaches to solving common programming problems. They're like problem-solving templates that you can adapt to different situations.

Why Learn Patterns?
  • Faster problem solving: Recognize patterns and apply known solutions
  • Better efficiency: Proven approaches that work well
  • Communication: Share solutions using common vocabulary
  • Interview success: Pattern recognition is key in technical interviews

🎲 1. Two Pointer Technique

Use two pointers (indices) to traverse an array from different positions. Common in array problems.

Example: Two Pointer - Palindrome Check
Algorithm PalindromeCheck

Function IsPalindrome(arr, size)
    var left = 0
    var right = size - 1
    
    While left < right Do
        If arr[left] != arr[right] Then
            Return false
        Endif
        
        left = left + 1
        right = right - 1
    Endwhile
    
    Return true
Endfunction

var test1 = [1, 2, 3, 2, 1]
var test2 = [1, 2, 3, 4, 5]

Print "[1,2,3,2,1] is palindrome:", IsPalindrome(test1, 5)
Print "[1,2,3,4,5] is palindrome:", IsPalindrome(test2, 5)

Endalgorithm

📦 2. Sliding Window

Maintain a "window" of elements and slide it through the array. Great for finding subarrays or sequences.

Example: Maximum Sum Subarray (Fixed Size)
Algorithm MaxSumWindow

var arr = [2, 1, 5, 1, 3, 2]
var windowSize = 3
var n = 6

# Calculate first window sum
var windowSum = 0
For i = 0 To windowSize - 1
    windowSum = windowSum + arr[i]
Endfor

var maxSum = windowSum

# Slide window through array
For i = windowSize To n - 1
    # Remove leftmost element, add new element
    windowSum = windowSum - arr[i - windowSize] + arr[i]
    
    If windowSum > maxSum Then
        maxSum = windowSum
    Endif
Endfor

Print "Maximum sum of 3 consecutive elements:", maxSum

Endalgorithm

🎲 3. Greedy Algorithm

Make the locally optimal choice at each step, hoping to find a global optimum. Works well for certain problems.

Example: Coin Change (Greedy)
Algorithm CoinChange

var amount = Input "Enter amount (cents):"

var coins = [25, 10, 5, 1]  # Quarters, dimes, nickels, pennies
var coinNames = ["quarters", "dimes", "nickels", "pennies"]
var coinCount[4]

Print ""
Print "=== MAKING CHANGE ==="
Print ""

For i = 0 To 3
    coinCount[i] = amount / coins[i]  # Integer division
    amount = amount mod coins[i]  # Remaining amount
    
    If coinCount[i] > 0 Then
        Print coinCount[i], coinNames[i]
    Endif
Endfor

Endalgorithm

🔄 4. Divide and Conquer

Break a problem into smaller sub-problems, solve them independently, then combine the results.

Example: Find Maximum (Divide & Conquer)
Algorithm DivideConquerMax

Function FindMax(arr, left, right)
    # BASE CASE: single element
    If left == right Then
        Return arr[left]
    Endif
    
    # DIVIDE: split into two halves
    var mid = (left + right) / 2
    
    # CONQUER: find max in each half
    var leftMax = FindMax(arr, left, mid)
    var rightMax = FindMax(arr, mid + 1, right)
    
    # COMBINE: return the greater of two maxes
    If leftMax > rightMax Then
        Return leftMax
    Else
        Return rightMax
    Endif
Endfunction

var numbers = [3, 41, 52, 26, 38, 57, 9, 49]
var max = FindMax(numbers, 0, 7)

Print "Maximum value:", max

Endalgorithm

5. Dynamic Programming (Introduction)

Dynamic Programming solves complex problems by breaking them down and storing solutions to sub-problems to avoid redundant calculations.

Example: Fibonacci with Memoization
Algorithm FibonacciDP

var memo[100]  # Store calculated values

# Initialize memo array
For i = 0 To 99
    memo[i] = -1  # -1 means not calculated yet
Endfor

Function Fib(n)
    # BASE CASES
    If n == 0 Then
        Return 0
    Else If n == 1 Then
        Return 1
    Endif
    
    # Check if already calculated
    If memo[n] != -1 Then
        Return memo[n]  # Return stored value
    Endif
    
    # Calculate and store
    memo[n] = Fib(n - 1) + Fib(n - 2)
    Return memo[n]
Endfunction

Print "Fibonacci numbers:"
For i = 0 To 10
    Print "F(", i, ") =", Fib(i)
Endfor

Endalgorithm

🔢 6. Counting Pattern

Track occurrences or frequencies of elements using counters or frequency arrays.

Example: Frequency Counter
Algorithm FrequencyCounter

var arr = [1, 2, 2, 3, 1, 4, 2, 5, 3, 1]
var frequency[6]  # For numbers 0-5

# Initialize frequency array
For i = 0 To 5
    frequency[i] = 0
Endfor

# Count occurrences
For i = 0 To Size(arr) - 1
    var value = arr[i]
    frequency[value] = frequency[value] + 1
Endfor

# Display frequencies
Print "=== FREQUENCY TABLE ==="
For i = 1 To 5
    If frequency[i] > 0 Then
        Print "Number", i, "appears", frequency[i], "times"
    Endif
Endfor

Endalgorithm

7. Pattern Matching

Search for patterns or subsequences within data.

Example: Find Pattern in Array
Algorithm PatternSearch

var data = [1, 2, 3, 4, 2, 3, 5, 6, 2, 3]
var pattern = [2, 3]

var dataSize = 10
var patternSize = 2

Print "Searching for pattern [2,3]..."
Print ""

var foundCount = 0

For i = 0 To dataSize - patternSize
    var match = true
    
    # Check if pattern matches at position i
    For j = 0 To patternSize - 1
        If data[i + j] != pattern[j] Then
            match = false
        Endif
    Endfor
    
    If match == true Then
        Print "✅ Pattern found at index", i
        foundCount = foundCount + 1
    Endif
Endfor

Print ""
Print "Total matches:", foundCount

Endalgorithm

📊 Pattern Selection Guide

Pattern When to Use Example Problems
Two Pointer Array traversal from both ends Palindrome, pair sum
Sliding Window Contiguous subarray problems Max sum, substring search
Greedy Local optimal leads to global Coin change, scheduling
Divide & Conquer Break into independent parts Merge sort, binary search
Dynamic Programming Overlapping sub-problems Fibonacci, shortest path

Key Takeaways

  • ✅ Design patterns are reusable problem-solving approaches
  • Two Pointer: Traverse from both ends
  • Sliding Window: Process contiguous subarrays
  • Greedy: Make optimal choice at each step
  • Divide & Conquer: Break into smaller problems
  • Dynamic Programming: Store solutions to avoid recalculation
  • ✅ Recognize patterns to solve problems faster

💪 Practice Exercises

Try These Exercises

Exercise 1: Two Sum Problem
Use two pointers to find two numbers in a sorted array that add up to a target sum.
Exercise 2: Maximum Average Subarray
Use sliding window to find the contiguous subarray of size K with the maximum average.
Exercise 3: Activity Selection
Use greedy algorithm to select maximum number of non-overlapping activities.
Exercise 4: Climbing Stairs
Use dynamic programming: ways to climb N stairs if you can take 1 or 2 steps at a time.
Outstanding Work!
You now understand powerful algorithm design patterns! In the final lesson, we'll cover best practices and optimization techniques to write professional-quality code.