Problem Solving: Intermediate
Spotify's playlist deduplication system uses sliding window and frequency counting patterns to scan billions of playlist entries for duplicate songs in linear time, cutting what would have been hours of processing into minutes across 4 billion user-created playlists. The sliding window technique avoids re-scanning already-processed data on every step, achieving O(n) time where a naive approach would take O(n²). Two-pointer and frequency counting are the most common patterns in technical interviews at companies like Stripe, Airbnb, and Shopify because they appear in real production systems every day. The patterns you learn in this lesson will show up repeatedly in both interviews and on the job.
Common Code Patterns
Apply transform, reduce, and two-pass
The Transform Pattern
Python provides a concise way to do this with [x for x in ...] list comprehensions:
The Reduce Pattern
The Two-Pass Pattern
> A class has test scores [85, 90, 78, 92, 88] and the teacher wants to see how each student compares to the average. Pick an expression to transform each score relative to the mean.
scores = [85, 90, 78, 92, 88] average = sum(scores) / len(scores) result = [ for s in scores] print(result)
Edge Case Handling
Guard every function against bad input
Defensive Programming
Common Pitfalls
- Check for empty input first
- Validate indices before access
- Use >= instead of > for bounds
- Handle None explicitly
- Divide without checking for zero
- Access list[-1] on empty lists
- Confuse < with <= in ranges
- Treat empty string as valid data
> This safe_get function uses > instead of >= to guard against out-of-range indices. When the index equals the list length, the check passes but the access fails.
IndexError: list index out of range when index equals len(items)
Off-by-one errors at boundaries are among the most common bugs in all of programming. Zero-based indexing means a list of n items has valid indices from 0 to n-1, so the guard condition must use >= len rather than > len.
Time Complexity Basics
Predict runtime with Big O notation
O(1) - Constant Time
Operations that take O(1) time run in the same time regardless of input size. Direct access, simple arithmetic, and dictionary lookups are all constant-time.
O(n) - Linear Time
O(n) time grows proportionally with input size. Scanning through a list once with a simple loop is a common O(n) operation.
O(n²) - Quadratic Time
O(n²) time grows with the square of input size. Nested loops over the same data are a warning sign of quadratic behavior.
Recognizing these three complexity classes (O(1), O(n), and O(n²)) helps you estimate how your code will perform before you even run it.
- O(1): 1 operation
- O(n): 1,000 operations
- O(n²): 1,000,000 operations
- O(1): 1 operation
- O(n): 1,000,000 operations
- O(n²): 1 trillion operations!
> You need to find pairs in [3, 1, 4, 1, 5] that sum to 6. Pick the inner logic for the loop and see whether each approach uses nested iteration or a smarter lookup.
items = [3, 1, 4, 1, 5] target = 6 for i in range(len(items)): print(f"Found pair") print("Search complete")
Recognizing O(n²) before you run your code comes from spotting nested loops over the same data. When you see two loops both ranging over the input, ask whether there is a data structure that can eliminate the inner loop.
A set converts the O(n) inner "in" check to O(1), turning the overall algorithm from O(n²) to O(n). This is one of the most common interview optimizations and one of the most impactful in production code.
set is often the simplest path from one to the other.Space Complexity Basics
Spot and fix wasted memory usage
O(1) Space
O(n) Space
> This function builds an entire list of positive numbers just to check if any exist. It wastes O(n) memory when a simple early return would suffice.
Code works but wastes memory by building a list just to check existence
Space and time complexity trade-offs are real design decisions. Sometimes using O(n) extra space (like a hash map) halves the time complexity. Other times, as shown here, using O(1) space also improves time by enabling early termination.
Choosing Data Structures
Pick list, set, or dict on sight
List vs Set vs Dictionary
- Frequent "is X in collection?" checks: use a
setfor O(1) membership - Need to count occurrences or group by key: use a
dict - Order matters and you access by position: use a
list - Need both fast lookup and ordering: build a
dictand keep a sortedlistof keys
Performance Comparison
The difference between O(1) and O(n) lookup is dramatic:
When you see if x in collection inside a loop, ask yourself: should this be a set?
> A function checks whether a list [1, 2, 3, 2] contains any duplicates. Pick the data structure and method that give O(1) membership testing for the fastest solution.
def has_duplicates(items): for item in items: if item in seen: return True seen.(item) return False print(has_duplicates([1, 2, 3, 2]))
Choosing the right data structure at the start is one of the highest-leverage decisions in software development. A set here reduces an O(n²) algorithm to O(n) with a single line change.
The decision framework is straightforward: if your algorithm's dominant operation is checking membership, use a set. If it's counting or grouping, use a dict. If it's ordered access by position, use a list.
if item in some_list" inside a loop, that is often a performance red flag. Converting the list to a set before the loop frequently eliminates the bottleneck.> You are a data engineer at a startup profiling a slow Python transformation script that times out on production datasets. You must select better algorithms and data structures to bring runtime within the performance budget.
list to a set for membership testing cuts lookup time from O(n) to O(1).Noneset for fast membership testing, dict for fast lookupWrite code that scales
- Category
- Python
- Difficulty
- intermediate
- Duration
- 21 minutes
- Challenges
- 0 hands-on challenges
Topics covered: Common Code Patterns, Edge Case Handling, Time Complexity Basics, Space Complexity Basics, Choosing Data Structures
Lesson Sections
- Common Code Patterns
Experienced programmers don't solve each problem from scratch. They recognize patterns and apply proven approaches. Here are essential patterns you'll use constantly. The Transform Pattern Convert each element into a new form. Use when you need to change every item in a collection. The Reduce Pattern Combine all elements into a single value. Use for totals, products, finding min/max, or building composite results. The Two-Pass Pattern Sometimes you need information from the entire collection bef
- Edge Case Handling
Edge cases are inputs at the boundaries of what's valid. They're where bugs hide and where robust code proves its worth. Professional developers think about edge cases BEFORE writing code. Defensive Programming Handle edge cases explicitly at the start of your functions. This makes your intentions clear and prevents crashes. Common Pitfalls These edge cases cause the most bugs: Defensive programming at function boundaries -- validating inputs before using them -- prevents a whole class of crashe
- Time Complexity Basics
Time complexity describes how runtime grows as input size increases. We use Big O notation to classify algorithms by their worst-case scaling behavior. O(1) - Constant Time O(n) - Linear Time O(n²) - Quadratic Time The difference between these classes becomes dramatic as input size grows. These numbers have real consequences when you run code on large datasets.
- Space Complexity Basics
Space complexity measures how much memory your algorithm uses relative to input size. Sometimes you can trade space for time, using more memory to run faster. O(1) Space Uses a fixed amount of memory regardless of input size. Only a few variables, no new collections. O(n) Space Memory grows with input size. Creating new lists, building results. Allocating memory proportional to input size when only a boolean answer is needed is a common beginner mistake. Early return reduces both time complexity
- Choosing Data Structures
The right data structure can make a problem trivial. The wrong one can make it impossibly slow. Here's how to choose. List vs Set vs Dictionary Each data structure excels in different scenarios. Choosing correctly often determines whether your solution is fast or slow. When you are unsure which structure fits, think about what your code does most often. The dominant operation should drive your choice. Performance Comparison Systematic problem-solving techniques help you tackle increasingly compl