By Gopi Krishna Tummala
Introduction
This is your senior-engineer cheatsheet to Python interview mastery—the advanced patterns, time-savers, and “interview-flex” moves that appear in ~70% of medium/hard questions. These are the gotchas, one-liners, edge cases, and power moves that interviewers notice (and that save time under pressure).
Memorize the why behind each trick—that’s what gets the hire verdict.
1. Priority Queues / heapq (Very Common)
Python’s heapq is min-heap only—interviewers almost always expect you to know how to fake a max-heap.
Min-Heap (Default)
import heapq
heap = [3, 1, 4]
heapq.heapify(heap) # O(n) — in-place!
heapq.heappush(heap, 2) # O(log n)
smallest = heapq.heappop(heap) # O(log n)
# Peek (O(1))
if heap:
heap[0] # smallest element
Max-Heap Trick (Most Common Interview Hack)
maxh = []
heapq.heappush(maxh, -10)
heapq.heappush(maxh, -5)
largest = -heapq.heappop(maxh) # → 10
Why it works: Negating values turns a min-heap into a max-heap. The smallest negative number is the largest positive number.
Tuple Heap
# Heap of tuples — compares FIRST element, then second, etc.
heapq.heappush(heap, (priority, index, value)) # stable sort by index if priorities equal
# For max-heap with tuples → negate priority only
heapq.heappush(maxh, (-priority, index, value))
Gotcha: Tuples compare left-to-right—great for tie-breaking.
K Smallest / K Largest Patterns
# K largest → min-heap of size K (keep popping small ones)
def kth_largest(nums, k):
heap = nums[:k]
heapq.heapify(heap)
for num in nums[k:]:
if num > heap[0]:
heapq.heapreplace(heap, num) # pop + push in one O(log k)
return heap[0] # smallest of the k largest
Merge K Sorted Lists (Classic)
heap = []
for i, lst in enumerate(lists):
if lst:
heapq.heappush(heap, (lst[0], i, 0)) # (val, list_idx, elem_idx)
Critical Gotchas Interviewers Test
heapify()is O(n) — faster than n pushes (O(n log n))- No built-in decrease-key → usually rebuild or use set+lazy delete pattern
- Tuples compare left-to-right — great for tie-breaking
2. Dynamic Programming Tricks & Memoization Hacks
Top-down (memo) vs bottom-up—know when to pick which.
@lru_cache — Fastest Way in Interviews
from functools import lru_cache
@lru_cache(maxsize=None) # None = unlimited
def fib(n):
if n <= 1: return n
return fib(n-1) + fib(n-2)
Why it’s better: Clean, fast, and shows you know Python’s standard library.
Manual Dict Memo (Shows Understanding)
def climbStairs(n, memo={}):
if n in memo: return memo[n]
if n <= 2: return n
memo[n] = climbStairs(n-1, memo) + climbStairs(n-2, memo)
return memo[n]
2D Memo — Tuple Keys (Very Common)
def uniquePaths(m, n, memo={}):
if (m,n) in memo: return memo[(m,n)]
if m == 1 or n == 1: return 1
memo[(m,n)] = uniquePaths(m-1,n,memo) + uniquePaths(m,n-1,memo)
return memo[(m,n)]
Bottom-Up Space-Optimized Tricks
0/1 Knapsack → O(W) Space
dp = [0] * (W+1)
for wt, val in items:
for w in range(W, wt-1, -1): # ← reverse! prevents using same item multiple times
dp[w] = max(dp[w], dp[w-wt] + val)
Why reverse loop: Prevents using the same item multiple times. If you iterate forward, you might use an item that was already updated in the same iteration.
House Robber / Max Non-Adjacent → Two Variables Only
prev2, prev1 = 0, 0
for num in nums:
curr = max(prev1, prev2 + num)
prev2, prev1 = prev1, curr
Digit DP Template
For problems asking “how many numbers between A and B satisfy X”:
@lru_cache(None)
def dp(index, is_less, is_started, current_val):
if index == len(s): return 1
limit = int(s[index]) if not is_less else 9
res = 0
for d in range(limit + 1):
res += dp(index + 1, is_less or d < limit, is_started or d > 0, ...)
return res
Rolling Array (Space Optimization)
If dp[i] only depends on dp[i-1], use two rows or a single array:
# Instead of dp[n][m]
prev_row = [0] * m
for i in range(n):
curr_row = [0] * m
for j in range(m):
curr_row[j] = prev_row[j] + ... # logic
prev_row = curr_row
Tricks Interviewers Notice
- Use tuple keys for multi-state memo
(i, remaining, tight, etc.) - Add state for constraints (digit DP: tight flag, leading zero flag)
- Bottom-up iteration order matters (reverse for 0/1 knapsack)
- Sometimes combine with greedy (jump game, stock problems)
3. Sorting — Advanced & Tricky Cases
Stable Sort
Python’s sorted() and list.sort() are stable—preserves original order on ties.
sorted(intervals, key=lambda x: x[0]) # if start times equal → original order preserved
Multi-Key Sort (Very Frequent)
# Sort by deadline (earliest first), then by profit (highest first)
sorted(tasks, key=lambda x: (x.deadline, -x.profit))
# Sort by length, then alphabetically
sorted(words, key=lambda w: (len(w), w))
Hack: sorted(..., key=lambda x: (-x[1], x[0])) → descending on second, ascending on first.
Custom Distance Sort
sorted(points, key=lambda p: p[0]**2 + p[1]**2)
Dutch National Flag / 3-Way Partition Trick
For “sort colors” problem (red < pivot < blue):
lo = mid = 0
hi = len(nums) - 1
while mid <= hi:
if nums[mid] == 0:
nums[lo], nums[mid] = nums[mid], nums[lo]
lo += 1
mid += 1
elif nums[mid] == 2:
nums[mid], nums[hi] = nums[hi], nums[mid]
hi -= 1
else:
mid += 1
4. Bitwise Operations — Interview Favorites & Hacks
Essential Bitwise Tricks
# Check if power of 2 → n > 0 and n & (n-1) == 0
# Check odd/even → n & 1
# Get last set bit → n & -n (two's complement trick)
# Count set bits (Brian Kernighan) → while n: n &= n-1; count +=1 → O(set bits)
# Flip last set bit → n &= n-1
# Set k-th bit → n | (1 << k)
# Clear k-th bit → n & ~(1 << k)
# Toggle k-th bit → n ^ (1 << k)
# Get k-th bit → (n >> k) & 1
Power of 2 Check
def is_power_of_2(n):
return n > 0 and n & (n-1) == 0
Why it works: Powers of 2 have exactly one set bit. n & (n-1) removes the lowest set bit, so if the result is 0, there was only one set bit.
Brian Kernighan’s Algorithm (Count Set Bits)
def count_set_bits(n):
count = 0
while n:
n &= n-1 # Remove lowest set bit
count += 1
return count
Complexity: O(set bits) instead of O(log n).
XOR Tricks
Swap Two Numbers
a ^= b
b ^= a
a ^= b
Missing Number (1..n)
xor_all = 0
for num in nums:
xor_all ^= num
for i in range(1, n+1):
xor_all ^= i
return xor_all
Single Number III (Two Unique Numbers)
xor = 0
for num in nums:
xor ^= num
rightmost = xor & -xor # rightmost set bit
a = b = 0
for num in nums:
if num & rightmost:
a ^= num
else:
b ^= num
return [a, b]
Bitmask DP
State as integer (subsets, digit DP):
# Iterate over all subsets
for mask in range(1 << n):
for i in range(n):
if mask & (1 << i):
# i included
Most Asked Bitwise Tricks
n & (n-1)→ remove lowest set bit (power of 2 check, subset DP)n & -n→ isolate lowest set bit- XOR for duplicate detection / missing number
- Bitmask DP → state as integer (subsets, digit DP)
5. Advanced Data Structures
deque (Double-Ended Queue)
from collections import deque
dq = deque()
dq.appendleft(1) # O(1)
dq.popleft() # O(1)
dq.append(2) # O(1)
dq.pop() # O(1)
Why use it: list.pop(0) is O(n), but deque.popleft() is O(1). Essential for BFS and sliding windows.
Counter (Frequency Maps)
from collections import Counter
c = Counter("mississippi")
c.most_common(3) # [('i',4), ('s',4), ('p',2)]
# Set operations
c1 & c2 # intersection
c1 - c2 # find differences
bisect (Binary Search on Arrays)
import bisect
# Find insertion point
idx = bisect.bisect_left(sorted_list, target)
# Find number of elements in range [low, high]
def count_in_range(sorted_list, low, high):
return bisect.bisect_right(sorted_list, high) - bisect.bisect_left(sorted_list, low)
defaultdict (Group by Key)
from collections import defaultdict
# Group by key in one line
groups = defaultdict(list)
for k, v in pairs:
groups[k].append(v)
# One-liner group anagrams (classic)
anagrams = defaultdict(list)
for w in words:
anagrams[tuple(sorted(w))].append(w)
6. Monotonic Stack / Queue Templates
Monotonic Increasing Stack (Next Greater Element)
def next_greater(nums):
stack = []
res = [-1] * len(nums)
for i, num in enumerate(nums):
while stack and nums[stack[-1]] < num:
idx = stack.pop()
res[idx] = num
stack.append(i)
return res
Used in:
- Next Greater Element
- Largest Rectangle in Histogram
- Daily Temperatures
Complexity: Each element pushed/popped once → O(n)
Sliding Window Maximum (Deque Trick)
from collections import deque
def max_sliding_window(nums, k):
dq = deque()
res = []
for i in range(len(nums)):
# remove out-of-window
if dq and dq[0] == i - k:
dq.popleft()
# maintain decreasing order
while dq and nums[dq[-1]] < nums[i]:
dq.pop()
dq.append(i)
if i >= k - 1:
res.append(nums[dq[0]])
return res
Why it works: Maintains indices in decreasing order of values. The front always has the maximum for the current window.
7. Prefix Sum & Difference Array
Prefix Sum
prefix = [0]
for num in nums:
prefix.append(prefix[-1] + num)
# Sum of nums[l:r]
range_sum = prefix[r] - prefix[l]
Difference Array (Range Update Trick)
Used in:
- Corporate Flight Bookings
- Range addition problems
diff = [0] * (n + 1)
for l, r, val in updates:
diff[l] += val
diff[r + 1] -= val
# Build final array
arr = []
curr = 0
for i in range(n):
curr += diff[i]
arr.append(curr)
Interview flex: O(n + q) instead of O(n*q) for q range updates.
8. Binary Search Patterns
Lower Bound Template
def lower_bound(nums, target):
l, r = 0, len(nums)
while l < r:
mid = (l + r) // 2
if nums[mid] < target:
l = mid + 1
else:
r = mid
return l
Binary Search on Answer (Very Common Hard)
Used in:
- Minimize maximum
- Allocate pages
- Koko Eating Bananas
def can(x):
# check if x works
return ...
l, r = low, high
while l < r:
mid = (l + r) // 2
if can(mid):
r = mid
else:
l = mid + 1
Interview insight: Think in terms of monotonic property. This pattern appears in ~25% of hard problems.
9. Graph Patterns
BFS Template
from collections import deque
def bfs(start):
q = deque([start])
visited = {start}
while q:
node = q.popleft()
for nei in graph[node]:
if nei not in visited:
visited.add(nei)
q.append(nei)
DFS Iterative (Interview Safe)
def dfs(start):
stack = [start]
visited = set()
while stack:
node = stack.pop()
if node in visited:
continue
visited.add(node)
stack.extend(graph[node])
Why use iterative: Safer than recursion in deep graphs. Avoids stack overflow.
10. Python Gotchas & Syntax Hacks
Mutable Default Arguments (Classic Trap)
def bad(x, lst=[]): # ← created ONCE at def time
lst.append(x)
return lst
print(bad(1)) # [1]
print(bad(2)) # [1, 2] ← surprise!
Fix interviewers expect:
def good(x, lst=None):
lst = lst or [] # or if lst is None: lst = []
lst.append(x)
return lst
List Multiplication Shares References
grid = [[0]*5]*4 # ← all 4 rows are the SAME list!
grid[0][0] = 99
print(grid) # all rows become [99,0,0,0,0]
Correct ways:
grid = [[0]*5 for _ in range(4)] # list comp = new lists
# or
grid = [[0 for _ in range(5)] for _ in range(4)] # safest
is vs == (and Small Int/String Interning)
a = 256; b = 256
print(a is b) # True (small ints cached)
a = 257; b = 257
print(a is b) # usually False (implementation detail!)
s1 = "hello"; s2 = "hello"
print(s1 is s2) # True (string interning)
lst1 = [1]; lst2 = [1]
print(lst1 is lst2) # False — always for mutable
Rule interviewers want: Use == for value, is for identity / None / True/False / singletons.
Walrus Operator := (3.8+) – Assignment Expressions
# Before
match = pattern.search(data)
if match:
print(match.group())
# Cleaner (interview flex)
if match := pattern.search(data):
print(match.group())
# Very common pattern
while (line := file.readline()):
process(line)
String Operations (Performance)
# Never do this (O(n²))
s = ""
for char in chars:
s += char
# Always do this (O(n))
s = "".join(chars)
# Fastest way to reverse/copy
reversed = A[::-1]
copy = A[:]
Late Binding Closures (Classic Gotcha)
funcs = [lambda x: x*i for i in range(4)]
print(funcs[0](5)) # 15 ← i=3 at call time, not creation!
Fix:
funcs = [lambda x, i=i: x*i for i in range(4)] # default captures current value
Multiple Assignment & Unpacking
# Multiple assignment + unpacking
a, b, *rest, z = [1,2,3,4,5,6] # a=1, b=2, rest=[3,4,5], z=6
# Swap without temp
a, b = b, a
Matrix Operations
# Transpose matrix
list(zip(*matrix))
# Flatten 2D list
flat = [x for row in matrix for x in row]
Unique Elements While Preserving Order
# Python 3.7+ dict keys ordered
list(dict.fromkeys(lst))
# Or with set (but loses order)
list(set(lst))
11. Performance Optimizations
Complexity Micro-Optimizations
| Instead of | Use | Why |
|---|---|---|
if x in list | set | O(1) vs O(n) |
list.pop(0) | deque.popleft() | O(1) vs O(n) |
+= string loop | "".join() | O(n) vs O(n²) |
| nested loops for freq | Counter | Cleaner & faster |
| manual heap build | heapify() | O(n) vs O(n log n) |
Check if Sorted
# One-liner
all(a <= b for a, b in zip(nums, nums[1:]))
# With itertools.pairwise (Python 3.10+)
from itertools import pairwise
all(a <= b for a, b in pairwise(nums))
12. Math Tricks
Ceil Division
ceil = (a + b - 1) // b
Modular Exponentiation
pow(base, exp, mod) # built-in, O(log n)
GCD / LCM
from math import gcd
lcm = a * b // gcd(a, b)
13. Common Patterns
Kadane’s Algorithm (Maximum Subarray Sum)
best = curr = nums[0]
for n in nums[1:]:
curr = max(n, curr + n)
best = max(best, curr)
return best
Remove Duplicates Preserving Order
seen = set()
[x for x in lst if not (x in seen or seen.add(x))]
14. Edge Cases to Consider
Always ask interviewers about:
- Empty input
- Single element
- Large constraints (10^5 / 10^6)
- Negative numbers in prefix sums
- Duplicate handling
- Overflow logic (in other languages)
Pro tip: Always ask:
“Can input contain duplicates? Negative numbers? Large constraints?”
Interviewers LOVE that proactive thinking.
15. Most Frequently Combined Patterns
| Problem Type | Combined Tricks |
|---|---|
| Hard array | prefix sum + hashmap |
| Hard string | sliding window + Counter |
| Hard DP | bitmask + memo |
| Hard graph | BFS + state compression |
| Hard greedy | sort + heap |
What Separates “Pass” vs “Strong Hire”
Strong hire candidates:
- Know time complexity instantly
- Choose correct data structure quickly
- Explain why reverse loop in knapsack
- Mention
heapify()is O(n) - Use tuple-key sorting naturally
- Handle edge cases proactively
- Ask clarifying questions about constraints
Memorization Checklist
Memorize these—they appear in ~70% of medium/hard questions:
- ✅ heapq max-heap negation
- ✅ 0/1 knapsack reverse loop
- ✅
n & (n-1) == 0for power of 2 - ✅
n & -nfor lowest bit - ✅ Mutable default arguments fix
- ✅ List multiplication gotcha
- ✅
isvs==rules - ✅ String concatenation →
"".join() - ✅
dequefor O(1) popleft - ✅ Tuple keys for multi-state memo
Practice explaining the why behind each trick—that’s what gets the hire verdict.
Conclusion
This cheatsheet covers the advanced patterns, gotchas, and optimizations that separate strong candidates from elite ones. These techniques appear in the majority of medium/hard interview questions.
Remember: Understanding the “why” is more important than memorizing the “what.” Interviewers want to see that you understand the underlying principles, not just that you can recall syntax.
Good luck in your interviews! 🚀