Exercises: Algorithms
Advanced Python algorithm exercises — sorting, searching, recursion, and problem-solving patterns.
Related chapters: Data Structures, Performance, Design Patterns
Exercise 1: Binary Search
Implement binary search on a sorted list. Return the index or -1 if not found.
binary_search([1, 3, 5, 7, 9, 11], 7) # 3
binary_search([1, 3, 5, 7, 9, 11], 4) # -1
Must be O(log n). Do not use Python’s bisect module.
Exercise 2: Bubble Sort
Implement bubble sort and count the number of swaps made.
sort_and_count([5, 3, 1, 4, 2])
# ([1, 2, 3, 4, 5], 7)
Then implement a version that stops early if the list is already sorted.
Exercise 3: Flatten Nested List
Flatten arbitrarily nested lists:
flatten([1, [2, 3], [4, [5, 6]], 7])
# [1, 2, 3, 4, 5, 6, 7]
Solve with recursion and with an iterative approach.
Exercise 4: Valid Parentheses
Check if a string of brackets is balanced:
is_valid("()[]{}") # True
is_valid("([)]") # False
is_valid("{[]}") # True
Use a stack.
Exercise 5: Longest Substring
Find the length of the longest substring without repeating characters:
longest_unique("abcabcbb") # 3 ("abc")
longest_unique("bbbbb") # 1 ("b")
longest_unique("pwwkew") # 3 ("wke")
Exercise 6: Merge Sorted Lists
Merge two sorted lists into one sorted list:
merge([1, 3, 5, 7], [2, 4, 6, 8])
# [1, 2, 3, 4, 5, 6, 7, 8]
Do it in O(n) time and O(n) space. Bonus: do it in-place if given linked lists.
Challenge: Rate Limiter
Implement a RateLimiter(max_calls, period_seconds) class:
limiter = RateLimiter(max_calls=3, period_seconds=10)
limiter.allow() # True
limiter.allow() # True
limiter.allow() # True
limiter.allow() # False — exceeded 3 calls in 10 seconds
# ... wait 10 seconds ...
limiter.allow() # True
This pattern is used in API gateways and web applications. See Design Patterns and Security.
Solution (Exercise 4)
def is_valid(s):
pairs = {")": "(", "]": "[", "}": "{"}
stack = []
for char in s:
if char in pairs:
if not stack or stack[-1] != pairs[char]:
return False
stack.pop()
else:
stack.append(char)
return not stack
Exercise 7: Quick Sort
Implement quicksort in-place. Return the sorted list.
quicksort([5, 3, 1, 4, 2]) # [1, 2, 3, 4, 5]
Exercise 8: BFS Shortest Path
Given an unweighted graph as adjacency dict, return shortest path length from start to end.
graph = {
"A": ["B", "C"],
"B": ["A", "D"],
"C": ["A", "D"],
"D": ["B", "C", "E"],
"E": ["D"],
}
shortest_path_length(graph, "A", "E") # 3
Hint
Use a queue for BFS. Track visited nodes and distance from start.