Skip to content
Gopi Krishna Tummala
Back

Python Interview Hacks 2026: Advanced Patterns, Syntax Tricks, and Gotchas

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 ofUseWhy
if x in listsetO(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 freqCounterCleaner & faster
manual heap buildheapify()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 TypeCombined Tricks
Hard arrayprefix sum + hashmap
Hard stringsliding window + Counter
Hard DPbitmask + memo
Hard graphBFS + state compression
Hard greedysort + 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) == 0 for power of 2
  • n & -n for lowest bit
  • ✅ Mutable default arguments fix
  • ✅ List multiplication gotcha
  • is vs == rules
  • ✅ String concatenation → "".join()
  • deque for 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! 🚀