Skip to main content

Python Pseudocode Writing Practice Problems & Exercises

Practice: Pseudocode Writing

10 problems3 Easy4 Medium3 Hard40–55 min
← Back to lesson

Easy

#1Translate Pseudocode: Find Maximum ElementEasy
pseudocode-to-pythonloopscomparison

Translate the following pseudocode into a working Python function. Do not use the built-in max() function.

FUNCTION find_max(numbers)
SET max_val TO numbers[0]
FOR EACH item IN numbers (starting from index 1)
IF item > max_val THEN
SET max_val TO item
END IF
END FOR
RETURN max_val
END FUNCTION
Solution
Python
def find_max(numbers):
    # SET max_val TO numbers[0]
    max_val = numbers[0]

    # FOR EACH item IN numbers (starting from index 1)
    for item in numbers[1:]:
        # IF item > max_val THEN SET max_val TO item
        if item > max_val:
            max_val = item

    # RETURN max_val
    return max_val

print(find_max([3, 7, 2, 9, 4]))   # 9
print(find_max([-5, -1, -8]))       # -1
print(find_max([42]))               # 42

Mapping: Every pseudocode line maps 1-to-1 to a Python line. SET x TO y becomes x = y. FOR EACH ... IN becomes a for loop. IF ... THEN becomes if:. This is why pseudocode is powerful — when the design is clear, the translation is mechanical.

def find_max(numbers):
    # Translate the pseudocode above into Python
    pass

print(find_max([3, 7, 2, 9, 4]))   # 9
print(find_max([-5, -1, -8]))       # -1
print(find_max([42]))               # 42
Expected Output
9\n-1\n42
Hints

Hint 1: The pseudocode sets max_val to the first element, then loops through the rest comparing each one.

Hint 2: In Python, you can iterate with `for item in numbers[1:]` to skip the first element you already assigned.

#2Design & Implement: Reverse a StringEasy
pseudocode-designstringsiteration

Your task: First write pseudocode for reversing a string (without using Python slicing [::-1] or reversed()), then implement it. Your pseudocode should handle the algorithm yourself, character by character.

Solution

Pseudocode:

FUNCTION reverse_string(text)
SET result TO empty string ""
SET index TO length of text - 1
WHILE index >= 0
APPEND text[index] TO result
DECREMENT index BY 1
END WHILE
RETURN result
END FUNCTION

Python implementation:

Python
def reverse_string(text):
    # SET result TO empty string
    result = ""
    # SET index TO length of text - 1
    index = len(text) - 1
    # WHILE index >= 0
    while index >= 0:
        # APPEND text[index] TO result
        result += text[index]
        # DECREMENT index BY 1
        index -= 1
    # RETURN result
    return result

print(reverse_string("hello"))    # olleh
print(reverse_string("Python"))   # nohtyP
print(reverse_string("a"))        # a

Key takeaway: Writing the pseudocode first made it obvious that we need a backward loop and an accumulator. The Python translation was straightforward once the logic was designed.

def reverse_string(text):
    # Step 1: Write your pseudocode as comments
    # Step 2: Implement each line
    pass

print(reverse_string("hello"))    # olleh
print(reverse_string("Python"))   # nohtyP
print(reverse_string("a"))        # a
Expected Output
olleh\nnohtyP\na
Hints

Hint 1: Think about building a new string by walking through the original backwards — or by appending characters in reverse order.

Hint 2: One approach: start with an empty result string, loop from the last index down to 0, and concatenate each character.

#3Translate Pseudocode: Count VowelsEasy
pseudocode-to-pythonstringscounting

Translate the following pseudocode into Python.

FUNCTION count_vowels(text)
SET vowels TO "aeiou"
SET count TO 0
FOR EACH character IN text
SET lower_char TO lowercase of character
IF lower_char IS IN vowels THEN
INCREMENT count BY 1
END IF
END FOR
RETURN count
END FUNCTION
Solution
Python
def count_vowels(text):
    # SET vowels TO "aeiou"
    vowels = "aeiou"
    # SET count TO 0
    count = 0
    # FOR EACH character IN text
    for character in text:
        # SET lower_char TO lowercase of character
        lower_char = character.lower()
        # IF lower_char IS IN vowels
        if lower_char in vowels:
            # INCREMENT count BY 1
            count += 1
    # RETURN count
    return count

print(count_vowels("Hello World"))          # 3
print(count_vowels("AEIOU aeiou"))          # 10
print(count_vowels("rhythm"))               # 0

Mapping notes: SET x TO y becomes assignment. IS IN becomes Python's in operator. INCREMENT count BY 1 becomes count += 1. The pseudocode made the case-insensitive logic explicit upfront — converting to lowercase before comparing.

def count_vowels(text):
    # Translate the pseudocode above into Python
    pass

print(count_vowels("Hello World"))          # 3
print(count_vowels("AEIOU aeiou"))          # 10
print(count_vowels("rhythm"))               # 0
Expected Output
3\n10\n0
Hints

Hint 1: The pseudocode converts each character to lowercase before checking — in Python use `.lower()` on the character.

Hint 2: The vowel set in Python can be a string "aeiou" and you check membership with `in`.


Medium

#4Design & Implement: Binary SearchMedium
pseudocode-designbinary-searchalgorithm

Write pseudocode for binary search on a sorted list, then translate it to Python. Your function should return the index of the target or -1 if not found.

Think through: What are the boundaries? When do you stop? How do you narrow the search space?

Solution

Pseudocode:

FUNCTION binary_search(sorted_list, target)
SET low TO 0
SET high TO length of sorted_list - 1

WHILE low <= high
SET mid TO (low + high) // 2
IF sorted_list[mid] EQUALS target THEN
RETURN mid
ELSE IF sorted_list[mid] < target THEN
SET low TO mid + 1
ELSE
SET high TO mid - 1
END IF
END WHILE

RETURN -1 (target not found)
END FUNCTION

Python implementation:

Python
def binary_search(sorted_list, target):
    # SET low TO 0
    low = 0
    # SET high TO length of sorted_list - 1
    high = len(sorted_list) - 1

    # WHILE low <= high
    while low <= high:
        # SET mid TO (low + high) // 2
        mid = (low + high) // 2

        # IF sorted_list[mid] EQUALS target
        if sorted_list[mid] == target:
            return mid
        # ELSE IF sorted_list[mid] < target
        elif sorted_list[mid] < target:
            low = mid + 1
        # ELSE (target is in left half)
        else:
            high = mid - 1

    # RETURN -1 (target not found)
    return -1

print(binary_search([1, 3, 5, 7, 9, 11, 13], 7))    # 3
print(binary_search([1, 3, 5, 7, 9, 11, 13], 4))    # -1
print(binary_search([2, 4, 6, 8, 10], 10))           # 4
print(binary_search([], 5))                           # -1

Why pseudocode helps here: Binary search has subtle off-by-one bugs. Writing the pseudocode first forces you to think through the loop invariant (low <= high), the midpoint calculation, and which boundary to update. The three branches (found, go right, go left) become obvious in pseudocode before you worry about Python syntax.

def binary_search(sorted_list, target):
    # Step 1: Write your pseudocode as comments
    # Step 2: Implement each line
    # Return the index of target, or -1 if not found
    pass

print(binary_search([1, 3, 5, 7, 9, 11, 13], 7))    # 3
print(binary_search([1, 3, 5, 7, 9, 11, 13], 4))    # -1
print(binary_search([2, 4, 6, 8, 10], 10))           # 4
print(binary_search([], 5))                           # -1
Expected Output
3\n-1\n4\n-1
Hints

Hint 1: Binary search maintains two pointers (low, high) and repeatedly checks the middle element — if the target is smaller, move high; if larger, move low.

Hint 2: Be careful with the midpoint calculation: use `(low + high) // 2`. The loop continues while low <= high.

#5Design & Implement: Retry with Exponential BackoffMedium
pseudocode-designretry-logicexponential-backoff

Design pseudocode for a retry mechanism with exponential backoff — a pattern used in every production system that calls external services. Then implement it in Python.

Requirements:

  • Call operation() up to max_retries times
  • On failure, wait base_delay * 2^attempt seconds (with small random jitter)
  • On success, return the result immediately
  • If all retries fail, raise the last exception
Solution

Pseudocode:

FUNCTION retry_with_backoff(operation, max_retries, base_delay)
FOR attempt FROM 0 TO max_retries - 1
TRY
SET result TO operation()
RETURN result (success — exit immediately)
CATCH exception AS error
IF attempt EQUALS max_retries - 1 THEN
RAISE error (last attempt — give up)
END IF
SET delay TO base_delay * (2 ^ attempt)
SET jitter TO random value between 0 and delay * 0.1
WAIT FOR delay + jitter seconds
END TRY
END FOR
END FUNCTION

Python implementation:

Python
import time
import random

def retry_with_backoff(operation, max_retries=3, base_delay=1.0):
    # FOR attempt FROM 0 TO max_retries - 1
    for attempt in range(max_retries):
        try:
            # SET result TO operation()
            result = operation()
            # RETURN result (success)
            return result
        except Exception as error:
            # IF attempt EQUALS max_retries - 1 THEN RAISE
            if attempt == max_retries - 1:
                raise
            # SET delay TO base_delay * (2 ^ attempt)
            delay = base_delay * (2 ** attempt)
            # SET jitter TO random value between 0 and delay * 0.1
            jitter = random.uniform(0, delay * 0.1)
            # WAIT FOR delay + jitter seconds
            time.sleep(delay + jitter)

# Test: operation that fails twice then succeeds
attempt_count = 0
def flaky_operation():
    global attempt_count
    attempt_count += 1
    if attempt_count < 3:
        raise ConnectionError(f"Failed on attempt {attempt_count}")
    return "Success!"

result = retry_with_backoff(flaky_operation, max_retries=5, base_delay=0.01)
print(result)
print(f"Took {attempt_count} attempts")

Production insight: The pseudocode made three critical design decisions explicit before we wrote any Python: (1) when to give up (last attempt check), (2) how to calculate delay (exponential formula), and (3) jitter to prevent thundering herd. These are exactly the things that cause bugs if you skip the design step.

import time
import random

def retry_with_backoff(operation, max_retries=3, base_delay=1.0):
    # Design pseudocode first, then implement
    # operation() may raise an exception
    # Return the result on success, or raise the last exception
    pass

# Test: operation that fails twice then succeeds
attempt_count = 0
def flaky_operation():
    global attempt_count
    attempt_count += 1
    if attempt_count < 3:
        raise ConnectionError(f"Failed on attempt {attempt_count}")
    return "Success!"

result = retry_with_backoff(flaky_operation, max_retries=5, base_delay=0.01)
print(result)
print(f"Took {attempt_count} attempts")
Expected Output
Success!\nTook 3 attempts
Hints

Hint 1: Exponential backoff means each retry waits longer: delay = base_delay * (2 ^ attempt_number). Add a small random jitter to avoid thundering herd.

Hint 2: Use a for loop from 0 to max_retries-1. Wrap the operation call in try/except. If it succeeds, return immediately. If it fails on the last attempt, re-raise.

#6Design & Implement: Password ValidatorMedium
pseudocode-designvalidationstring-analysis

Write pseudocode for a password validator that checks all four rules and collects all violations (not just the first one):

  1. Minimum 8 characters
  2. At least one uppercase letter
  3. At least one digit
  4. At least one special character (!@#$%^&*()_+-=)

Return a tuple of (is_valid, error_list).

Solution

Pseudocode:

FUNCTION validate_password(password)
SET errors TO empty list
SET special_chars TO "!@#$%^&*()_+-="

IF length of password < 8 THEN
APPEND "Too short (min 8 characters)" TO errors
END IF

SET has_upper TO false
SET has_digit TO false
SET has_special TO false

FOR EACH char IN password
IF char is uppercase THEN SET has_upper TO true
IF char is digit THEN SET has_digit TO true
IF char IS IN special_chars THEN SET has_special TO true
END FOR

IF has_upper IS false THEN
APPEND "No uppercase letter" TO errors
END IF
IF has_digit IS false THEN
APPEND "No digit" TO errors
END IF
IF has_special IS false THEN
APPEND "No special character" TO errors
END IF

SET is_valid TO (errors is empty)
RETURN (is_valid, errors)
END FUNCTION

Python implementation:

Python
def validate_password(password):
    # SET errors TO empty list
    errors = []
    # SET special_chars TO "!@#$%^&*()_+-="
    special_chars = "!@#$%^&*()_+-="

    # IF length of password < 8
    if len(password) < 8:
        errors.append("Too short (min 8 characters)")

    # SET flags TO false
    has_upper = False
    has_digit = False
    has_special = False

    # FOR EACH char IN password
    for char in password:
        if char.isupper():
            has_upper = True
        if char.isdigit():
            has_digit = True
        if char in special_chars:
            has_special = True

    # Check each flag, append error if false
    if not has_upper:
        errors.append("No uppercase letter")
    if not has_digit:
        errors.append("No digit")
    if not has_special:
        errors.append("No special character")

    # SET is_valid TO (errors is empty)
    is_valid = len(errors) == 0
    return (is_valid, errors)

result1 = validate_password("Str0ng!Pass")
print(result1)

result2 = validate_password("weak")
print(result2)

result3 = validate_password("NoDigitsHere!")
print(result3)

Design lesson: The pseudocode made a key UX decision explicit: check all rules and collect all errors, rather than returning on the first failure. This is the difference between a frustrating validator ("fix this... now fix this other thing... now fix this too...") and a helpful one. The pseudocode stage is where you make these decisions.

def validate_password(password):
    # Design pseudocode first, then implement
    # Return (is_valid: bool, errors: list of strings)
    pass

result1 = validate_password("Str0ng!Pass")
print(result1)

result2 = validate_password("weak")
print(result2)

result3 = validate_password("NoDigitsHere!")
print(result3)
Expected Output
(True, [])\n(False, ['Too short (min 8 characters)', 'No uppercase letter', 'No digit', 'No special character'])\n(False, ['No digit'])
Hints

Hint 1: Design your pseudocode to check four conditions independently (not short-circuit). Collect all errors in a list so the user sees everything wrong at once.

Hint 2: For special characters, define a set like "!@#$%^&*()_+-=" and check if any character in the password belongs to it.

#7Design & Implement: Token Bucket Rate LimiterMedium
pseudocode-designrate-limitersystem-design

Design pseudocode for a token bucket rate limiter — a classic system design component. Then implement it as a Python class.

How it works:

  • The bucket holds up to capacity tokens, starting full
  • Tokens are added at refill_rate tokens per second
  • Each request consumes 1 token — allowed if tokens available, denied otherwise
Solution

Pseudocode:

CLASS TokenBucket
CONSTRUCTOR(capacity, refill_rate)
SET self.capacity TO capacity
SET self.refill_rate TO refill_rate
SET self.tokens TO capacity (start full)
SET self.last_refill_time TO current time
END CONSTRUCTOR

METHOD allow_request()
CALL refill()
IF self.tokens >= 1 THEN
DECREMENT self.tokens BY 1
RETURN true
ELSE
RETURN false
END IF
END METHOD

METHOD refill()
SET now TO current time
SET elapsed TO now - self.last_refill_time
SET new_tokens TO elapsed * self.refill_rate
SET self.tokens TO min(self.tokens + new_tokens, self.capacity)
SET self.last_refill_time TO now
END METHOD
END CLASS

Python implementation:

Python
import time

class TokenBucket:
    def __init__(self, capacity, refill_rate):
        # SET self.capacity, refill_rate
        self.capacity = capacity
        self.refill_rate = refill_rate
        # SET self.tokens TO capacity (start full)
        self.tokens = capacity
        # SET self.last_refill_time TO current time
        self.last_refill_time = time.monotonic()

    def _refill(self):
        # SET now TO current time
        now = time.monotonic()
        # SET elapsed TO now - self.last_refill_time
        elapsed = now - self.last_refill_time
        # SET new_tokens TO elapsed * self.refill_rate
        new_tokens = elapsed * self.refill_rate
        # SET self.tokens TO min(tokens + new_tokens, capacity)
        self.tokens = min(self.tokens + new_tokens, self.capacity)
        # SET self.last_refill_time TO now
        self.last_refill_time = now

    def allow_request(self):
        # CALL refill
        self._refill()
        # IF self.tokens >= 1
        if self.tokens >= 1:
            self.tokens -= 1
            return True
        else:
            return False

# Test
bucket = TokenBucket(capacity=3, refill_rate=1)
results = []
for i in range(5):
    results.append(bucket.allow_request())
print(results)

# Wait for refill
time.sleep(0.05)
bucket2 = TokenBucket(capacity=2, refill_rate=100)
print(bucket2.allow_request())  # True
print(bucket2.allow_request())  # True
print(bucket2.allow_request())  # False (bucket empty)
time.sleep(0.02)                # wait for ~2 tokens
print(bucket2.allow_request())  # True (refilled)

System design insight: The pseudocode separated the rate limiter into two concerns — refill logic and request logic. This separation made the implementation clean: _refill() handles the time math, allow_request() handles the token check. In a real system, you would also need thread safety (a lock around _refill and token decrement), but the pseudocode design phase is where you identify that need.

import time

class TokenBucket:
    def __init__(self, capacity, refill_rate):
        # capacity: max tokens in the bucket
        # refill_rate: tokens added per second
        pass

    def allow_request(self):
        # Return True if request is allowed, False otherwise
        pass

# Test
bucket = TokenBucket(capacity=3, refill_rate=1)
results = []
for i in range(5):
    results.append(bucket.allow_request())
print(results)

# Wait for refill
time.sleep(0.05)  # small sleep for test speed
bucket2 = TokenBucket(capacity=2, refill_rate=100)  # fast refill for testing
print(bucket2.allow_request())  # True
print(bucket2.allow_request())  # True
print(bucket2.allow_request())  # False (bucket empty)
time.sleep(0.02)                # wait for ~2 tokens
print(bucket2.allow_request())  # True (refilled)
Expected Output
[True, True, True, False, False]\nTrue\nTrue\nFalse\nTrue
Hints

Hint 1: The bucket starts full. Each request removes one token. Tokens refill over time based on elapsed seconds since last check.

Hint 2: In allow_request: first calculate how many tokens to add based on elapsed time, then cap at capacity, then check if tokens >= 1.


Hard

#8Design & Implement: LRU CacheHard
pseudocode-designlru-cachedata-structures

Write pseudocode for an LRU (Least Recently Used) Cache, then implement it using Python's OrderedDict. This is a top interview question at every major tech company.

Requirements:

  • get(key) — return value if exists (and mark as recently used), else return -1
  • put(key, value) — insert or update (and mark as recently used). If over capacity, evict the least recently used entry.
  • Both operations must be O(1).
Solution

Pseudocode:

CLASS LRUCache
CONSTRUCTOR(capacity)
SET self.capacity TO capacity
SET self.cache TO new OrderedDict (maintains insertion order)
END CONSTRUCTOR

METHOD get(key)
IF key NOT IN self.cache THEN
RETURN -1
END IF
MOVE key TO end of self.cache (mark as most recently used)
RETURN self.cache[key]
END METHOD

METHOD put(key, value)
IF key IN self.cache THEN
MOVE key TO end of self.cache (mark as most recently used)
SET self.cache[key] TO value
ELSE
IF length of self.cache >= self.capacity THEN
REMOVE first item from self.cache (least recently used)
END IF
SET self.cache[key] TO value (added at end = most recent)
END IF
END METHOD
END CLASS

Python implementation:

Python
from collections import OrderedDict

class LRUCache:
    def __init__(self, capacity):
        # SET self.capacity and self.cache
        self.capacity = capacity
        self.cache = OrderedDict()

    def get(self, key):
        # IF key NOT IN self.cache THEN RETURN -1
        if key not in self.cache:
            return -1
        # MOVE key TO end (mark as most recently used)
        self.cache.move_to_end(key)
        # RETURN self.cache[key]
        return self.cache[key]

    def put(self, key, value):
        # IF key IN self.cache (update existing)
        if key in self.cache:
            # MOVE key TO end (mark as most recently used)
            self.cache.move_to_end(key)
            self.cache[key] = value
        else:
            # IF over capacity, REMOVE first item (LRU)
            if len(self.cache) >= self.capacity:
                self.cache.popitem(last=False)
            # SET self.cache[key] TO value
            self.cache[key] = value

# Test
cache = LRUCache(2)
cache.put("a", 1)
cache.put("b", 2)
print(cache.get("a"))     # 1  (a is now most recently used)
cache.put("c", 3)         # evicts "b" (least recently used)
print(cache.get("b"))     # -1 (evicted)
print(cache.get("c"))     # 3
cache.put("d", 4)         # evicts "a"
print(cache.get("a"))     # -1 (evicted)
print(cache.get("c"))     # 3
print(cache.get("d"))     # 4

Why pseudocode matters here: The LRU cache has a subtle invariant — the "end" of the ordered dict is always the most recently used, and the "front" is the least recently used. The pseudocode made this ordering strategy explicit with MOVE key TO end. Without this design step, it is easy to get the eviction direction wrong (removing the wrong end).

from collections import OrderedDict

class LRUCache:
    def __init__(self, capacity):
        pass

    def get(self, key):
        # Return value if key exists, else -1
        pass

    def put(self, key, value):
        # Insert or update. Evict least-recently-used if over capacity.
        pass

# Test
cache = LRUCache(2)
cache.put("a", 1)
cache.put("b", 2)
print(cache.get("a"))     # 1  (a is now most recently used)
cache.put("c", 3)         # evicts "b" (least recently used)
print(cache.get("b"))     # -1 (evicted)
print(cache.get("c"))     # 3
cache.put("d", 4)         # evicts "a"
print(cache.get("a"))     # -1 (evicted)
print(cache.get("c"))     # 3
print(cache.get("d"))     # 4
Expected Output
1\n-1\n3\n-1\n3\n4
Hints

Hint 1: OrderedDict remembers insertion order. The key operation is `move_to_end(key)` — call it on every get and put to mark the key as most recently used.

Hint 2: When the cache exceeds capacity, call `popitem(last=False)` to remove the first (oldest/least-recently-used) item.

#9Design & Implement: State Machine (Traffic Light)Hard
pseudocode-designstate-machinesystem-design

Design pseudocode for a state machine that models a traffic light with normal cycling and an emergency mode. Then implement it.

States: GREENYELLOWREDGREEN (normal cycle), plus FLASHING_RED (emergency).

Rules:

  • next() advances through the normal cycle (GREEN → YELLOW → RED → GREEN)
  • handle_emergency() enters FLASHING_RED from any state
  • In FLASHING_RED, next() does nothing (stays in FLASHING_RED)
  • resume() returns to RED (the safest state to resume from)
Solution

Pseudocode:

CLASS TrafficLight
DEFINE transitions AS:
"GREEN" -> "YELLOW"
"YELLOW" -> "RED"
"RED" -> "GREEN"
"FLASHING_RED" -> "FLASHING_RED" (no transition)

CONSTRUCTOR()
SET self.state TO "GREEN"
END CONSTRUCTOR

METHOD get_state()
RETURN self.state
END METHOD

METHOD next()
SET self.state TO transitions[self.state]
END METHOD

METHOD handle_emergency()
SET self.state TO "FLASHING_RED"
END METHOD

METHOD resume()
IF self.state EQUALS "FLASHING_RED" THEN
SET self.state TO "RED" (safest state to resume)
END IF
END METHOD
END CLASS

Python implementation:

Python
class TrafficLight:
    # DEFINE transitions
    TRANSITIONS = {
        "GREEN":        "YELLOW",
        "YELLOW":       "RED",
        "RED":          "GREEN",
        "FLASHING_RED": "FLASHING_RED",   # no transition in emergency
    }

    def __init__(self):
        # SET self.state TO "GREEN"
        self.state = "GREEN"

    def get_state(self):
        # RETURN self.state
        return self.state

    def next(self):
        # SET self.state TO transitions[self.state]
        self.state = self.TRANSITIONS[self.state]

    def handle_emergency(self):
        # SET self.state TO "FLASHING_RED"
        self.state = "FLASHING_RED"

    def resume(self):
        # IF self.state EQUALS "FLASHING_RED"
        if self.state == "FLASHING_RED":
            # SET self.state TO "RED" (safest state to resume)
            self.state = "RED"

# Test normal cycle
light = TrafficLight()
print(light.get_state())    # GREEN
light.next()
print(light.get_state())    # YELLOW
light.next()
print(light.get_state())    # RED
light.next()
print(light.get_state())    # GREEN

# Test emergency
light.handle_emergency()
print(light.get_state())    # FLASHING_RED
light.next()                # should stay in FLASHING_RED
print(light.get_state())    # FLASHING_RED
light.resume()
print(light.get_state())    # RED

State machine design lesson: The pseudocode captured the entire system in a single transition table — a dictionary mapping current state to next state. This is the canonical way to implement state machines: data-driven transitions instead of nested if/else chains. The pseudocode design phase is where you realize that FLASHING_RED should map to itself (no transition), and that resume should go to RED (not back to the previous state, which could be dangerous).

class TrafficLight:
    def __init__(self):
        pass

    def get_state(self):
        # Return current state as string
        pass

    def next(self):
        # Advance to next state
        pass

    def handle_emergency(self):
        # Switch to flashing red mode
        pass

    def resume(self):
        # Return to normal operation from emergency mode
        pass

# Test normal cycle
light = TrafficLight()
print(light.get_state())    # GREEN
light.next()
print(light.get_state())    # YELLOW
light.next()
print(light.get_state())    # RED
light.next()
print(light.get_state())    # GREEN

# Test emergency
light.handle_emergency()
print(light.get_state())    # FLASHING_RED
light.next()                # should stay in FLASHING_RED
print(light.get_state())    # FLASHING_RED
light.resume()
print(light.get_state())    # RED
Expected Output
GREEN\nYELLOW\nRED\nGREEN\nFLASHING_RED\nFLASHING_RED\nRED
Hints

Hint 1: Define the transitions as a dictionary mapping each state to the next state. FLASHING_RED is a special state that does not transition on next().

Hint 2: For resume(), the state machine should return to RED (the safest state) rather than wherever it was before the emergency.

#10Design & Implement: Merge SortHard
pseudocode-designmerge-sortrecursiondivide-and-conquer

Write pseudocode for merge sort — the classic divide-and-conquer sorting algorithm — then implement it with comments that map each line back to your pseudocode.

This is a two-part design challenge:

  1. The recursive merge_sort function (split and recurse)
  2. The merge helper function (combine two sorted lists)
Solution

Pseudocode:

FUNCTION merge_sort(arr)
--- Base case ---
IF length of arr <= 1 THEN
RETURN arr
END IF

--- Split ---
SET mid TO length of arr // 2
SET left_half TO merge_sort(arr[0 .. mid])
SET right_half TO merge_sort(arr[mid .. end])

--- Merge ---
RETURN merge(left_half, right_half)
END FUNCTION

FUNCTION merge(left, right)
SET result TO empty list
SET i TO 0 (pointer for left)
SET j TO 0 (pointer for right)

--- Compare and merge ---
WHILE i < length of left AND j < length of right
IF left[i] <= right[j] THEN
APPEND left[i] TO result
INCREMENT i
ELSE
APPEND right[j] TO result
INCREMENT j
END IF
END WHILE

--- Append remaining elements ---
APPEND left[i .. end] TO result
APPEND right[j .. end] TO result

RETURN result
END FUNCTION

Python implementation:

Python
def merge_sort(arr):
    # --- Base case ---
    # IF length of arr <= 1 THEN RETURN arr
    if len(arr) <= 1:
        return arr

    # --- Split ---
    # SET mid TO length of arr // 2
    mid = len(arr) // 2

    # SET left_half TO merge_sort(arr[0 .. mid])
    left_half = merge_sort(arr[:mid])

    # SET right_half TO merge_sort(arr[mid .. end])
    right_half = merge_sort(arr[mid:])

    # --- Merge ---
    # RETURN merge(left_half, right_half)
    return merge(left_half, right_half)


def merge(left, right):
    # SET result TO empty list
    result = []
    # SET i TO 0 (pointer for left)
    i = 0
    # SET j TO 0 (pointer for right)
    j = 0

    # --- Compare and merge ---
    # WHILE i < length of left AND j < length of right
    while i < len(left) and j < len(right):
        # IF left[i] <= right[j]
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1

    # --- Append remaining elements ---
    # APPEND left[i .. end] TO result
    result.extend(left[i:])
    # APPEND right[j .. end] TO result
    result.extend(right[j:])

    # RETURN result
    return result


print(merge_sort([38, 27, 43, 3, 9, 82, 10]))
print(merge_sort([5, 1]))
print(merge_sort([1]))
print(merge_sort([]))
print(merge_sort([4, 4, 2, 2, 1, 1]))

Divide-and-conquer insight: The pseudocode forced us to separate the algorithm into two distinct responsibilities: merge_sort handles the recursive splitting, merge handles combining sorted halves. Every line of pseudocode maps directly to Python. Notice how <= in the merge comparison (not <) preserves stability — equal elements keep their original relative order. This is a detail that is easy to get wrong without thinking through the pseudocode first.

def merge_sort(arr):
    # Design pseudocode first
    # Implement with comments mapping back to pseudocode steps
    pass

print(merge_sort([38, 27, 43, 3, 9, 82, 10]))
print(merge_sort([5, 1]))
print(merge_sort([1]))
print(merge_sort([]))
print(merge_sort([4, 4, 2, 2, 1, 1]))
Expected Output
[3, 9, 10, 27, 38, 43, 82]\n[1, 5]\n[1]\n[]\n[1, 1, 2, 2, 4, 4]
Hints

Hint 1: Merge sort has two phases: (1) recursively split the list in half until you have single elements, (2) merge two sorted halves back together. Write pseudocode for both the split step and the merge step separately.

Hint 2: The merge step uses two pointers (i for left, j for right). Compare left[i] vs right[j], take the smaller one, advance that pointer. When one side is exhausted, append the remainder of the other.

© 2026 EngineersOfAI. All rights reserved.