Python Tips for Technical Problems
I've recently been brushing up on LeetCode-style questions and I thought I'd share some handy tips that I almost always use in my solutions.
1. enumerate and zip functions
For replacing manual indexing or iterating in parallel.
These two functions are especially helpful when you want to traverse iterables. enumerate lets you access both the index and the value at the same time, removing the need for manual indexing. zip combines multiple iterables into a single iterator to traverse them simultaneously.
a = [1, 3, 5]
b = [2, 4, 6]
for idx, n in enumerate(a):
print(idx, n)
# Output:
# 0 1
# 1 3
# 2 5
for x, y in zip(a, b):
print(x, y)
# Output:
# 1 2
# 3 4
# 5 6
2. collections.defaultdict
For tallying counts or building adjacency lists.
Defaultdict automatically creates missing keys with a default value. This eliminates the need for existence checks and also prevents KeyError.
The most common default values used are usually int (to keep track of an ongoing count) and list (to build adjacency lists for graph-related problems).
from collections import defaultdict
words = ["apple", "banana", "apple", "orange", "banana", "apple"]
counts = defaultdict(int)
for word in words:
counts[word] += 1
print(counts)
# Output: defaultdict(<class 'int'>, {'apple': 3, 'banana': 2, 'orange': 1})
# Adjacency list with defaultdict(list)
from collections import defaultdict
edges = [(1, 2), (1, 3), (2, 4)]
graph = defaultdict(list)
for u, v in edges:
graph[u].append(v)
3. collections.Counter
For frequency analysis.
Counter is a dictionary subclass specialized for counting occurrences in an iterable. In a similar fashion to defaultdict(int), reading a missing key returns 0.
Building off the previous example, instead of manually looping, you could pass the iterable directly into a Counter object.
from collections import Counter
words = ["apple", "banana", "apple", "orange", "banana", "apple"]
counts = Counter(words)
print(counts)
# Output: Counter({'apple': 3, 'banana': 2, 'orange': 1})
4. bisect and insort
For finding a position in a sorted list or inserting into sorted lists.
The bisect module provides efficient ways to insert items into sorted lists while maintaining their sorted order. Instead of manually finding the correct insertion point with
linear search (O(n)), these functions use binary search for O(log n) performance.
Note that insort inserts in place at that index therefore overall insertion is O(n) due to shifting.
bisect_left and insort_left
Everything on the LEFT of the insertion point is strictly less than your number.
import bisect
arr = [1, 3, 4, 5]
index = bisect.bisect_left(arr, 4)
print(index)
# Output: 2
bisect.insort_left(arr, 4)
print(arr)
# Output: [1, 3, 4, 4, 5]
bisect_right and insort_right
Everything on the RIGHT of the insertion point is strictly greater than your number.
import bisect
arr = [1, 3, 4, 5]
index = bisect.bisect_right(arr, 4)
print(index)
# Output: 3
bisect.insort_right(arr, 4)
print(arr)
# Output: [1, 3, 4, 4, 5]
5. functools.cache
Use for functions when you have overlapping subproblems.
This decorator from the functools module stores results of function calls based off the arguments, which you may have heard of as a dynamic programming technique
called memoization.
Repeated calls with the same inputs return instantly without re-executing the function body.
Note: functools.cache requires Python 3.9+. For older versions, use functools.lru_cache(maxsize=None).
from functools import cache
@cache
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(10))
# Output: 55
6. pow for modular exponentiation
Use for fast modular exponentiation in number theory/combinatorics.
On rare occasions problems require computing large exponentials (like a^b) while keeping results within bounds using modular arithmetic.
Using ** and % can be slow, so it’s a lot more faster to use Python’s built-in pow(a, b, m), which performs efficient modular exponentiation.
# slower
result = (a ** b) % m
# faster
result = pow(a, b, m)
Conclusion
Whether you’re preparing for technical assessments or just aiming to write clearer and faster Python, I hope these tips help. The more you practice and incorporate these patterns, the more naturally they’ll come to mind. Good luck and happy coding!