Python · 120 hoursDSA Interview Lab
0 read
Streak0d
Python · 120 hours · visual edition

DSA Interview Lab

A complete A→Z Python DSA study site for interviews: 23 topics, static visual explanations, reusable templates, a 40-day plan, LeetCode practice and browser-saved progress — without a heavy runtime.

120h
course
23
topics
205
problems
40
days

1Your dashboard

0/23
topics read
0/0
problems solved
0h
plan hours checked
0/40
days done
0d
daily streak
Next study block
Start with Day 1.

2How to use the visual guides

  1. Read the intuition and identify the problem signal.
  2. Study the static visual board until you can explain what moves or changes without looking at code.
  3. Rewrite the Python template from memory, then solve the core LeetCode set.
  4. Mark the topic read only after you can state its invariant and complexity aloud.
Why static visuals?

The original traced-Python lab was intentionally removed to keep this GitHub Pages project small, fast and dependable. The teaching content, diagrams, progress tracking and practice workflow remain.

3Topic map

4Progress backup

Progress is stored in this browser. Export a JSON backup before clearing browser storage or changing devices.

40 days · 3 hours/day

The 120-hour plan

At 3 hours/day this is 40 study days. At 2 hours/day, expect roughly 9 weeks. Keep the order unless a mock exposes a weak prerequisite.

Daily rhythm

About one third concept/visual review, one third coding templates and examples, one third core problem solving. On large topics, the split naturally carries across several days.

W1Week 1 · days 1–5

Day 13h
  • Python 3h · Python — read concepts, study static visuals, code templates · core practice: 1, 217, 242, 49
Day 23h
  • Big-O 3h · Big-O — read concepts, study static visuals, code templates · core practice: 704, 69, 509
Day 33h
  • Arrays & Strings 3h · Arrays & Strings — read concepts, study static visuals, code templates
Day 43h
  • Arrays & Strings 3h · Arrays & Strings — read concepts, study static visuals, code templates
Day 53h

W2Week 2 · days 6–10

Day 63h
  • Hashing 3h · Hashing — read concepts, study static visuals, code templates
Day 73h
Day 83h
  • Two Pointers 3h · Two Pointers — read concepts, study static visuals, code templates
Day 93h
  • Two Pointers 2h · Two Pointers — read concepts, study static visuals, code templates · core practice: 125, 344, 26, 283, 977, 167, 15
  • Sliding Window 1h · Sliding Window — read concepts, study static visuals, code templates
Day 103h
  • Sliding Window 3h · Sliding Window — read concepts, study static visuals, code templates

W3Week 3 · days 11–15

Day 113h
  • Sliding Window 2h · Sliding Window — read concepts, study static visuals, code templates · core practice: 643, 3, 209, 424, 567, 1004, 76, 239
  • Stacks 1h · Stacks — read concepts, study static visuals, code templates
Day 123h
  • Stacks 3h · Stacks — read concepts, study static visuals, code templates
Day 133h
  • Stacks 1h · Stacks — read concepts, study static visuals, code templates · core practice: 20, 155, 150, 739, 496, 84
  • Queues 2h · Queues — read concepts, study static visuals, code templates · core practice: 232, 933, 622
Day 143h
  • Linked Lists 3h · Linked Lists — read concepts, study static visuals, code templates
Day 153h

W4Week 4 · days 16–20

Day 163h
  • Recursion 3h · Recursion — read concepts, study static visuals, code templates
Day 173h
  • Recursion 1h · Recursion — read concepts, study static visuals, code templates · core practice: 509, 50, 344, 24
  • Backtracking 2h · Backtracking — read concepts, study static visuals, code templates
Day 183h
  • Backtracking 3h · Backtracking — read concepts, study static visuals, code templates
Day 193h
  • Backtracking 1h · Backtracking — read concepts, study static visuals, code templates · core practice: 78, 46, 77, 39, 90, 22, 79, 51
  • Sorting 2h · Sorting — read concepts, study static visuals, code templates
Day 203h
  • Sorting 3h · Sorting — read concepts, study static visuals, code templates · core practice: 912, 75, 215, 148, 56, 347

W5Week 5 · days 21–25

Day 213h
  • Binary Search 3h · Binary Search — read concepts, study static visuals, code templates
Day 223h
Day 233h
  • Trees 3h · Trees — read concepts, study static visuals, code templates
Day 243h
  • Trees 3h · Trees — read concepts, study static visuals, code templates
Day 253h
  • Trees 2h · Trees — read concepts, study static visuals, code templates · core practice: 104, 94, 102, 226, 543, 236, 112, 101, 100, 199
  • BST 1h · BST — read concepts, study static visuals, code templates

W6Week 6 · days 26–30

Day 263h
  • BST 2h · BST — read concepts, study static visuals, code templates · core practice: 700, 701, 98, 230
  • Heaps 1h · Heaps — read concepts, study static visuals, code templates
Day 273h
  • Heaps 3h · Heaps — read concepts, study static visuals, code templates · core practice: 215, 703, 973, 1046, 295
Day 283h
  • Graphs 3h · Graphs — read concepts, study static visuals, code templates
Day 293h
  • Graphs 3h · Graphs — read concepts, study static visuals, code templates
Day 303h
  • Graphs 3h · Graphs — read concepts, study static visuals, code templates

W7Week 7 · days 31–35

Day 313h
Day 323h
  • Shortest Paths 1h · Shortest Paths — read concepts, study static visuals, code templates · core practice: 743, 787, 1584, 1631
  • Greedy 2h · Greedy — read concepts, study static visuals, code templates
Day 333h
  • Greedy 2h · Greedy — read concepts, study static visuals, code templates · core practice: 455, 55, 45, 134, 763
  • Dynamic Prog. 1h · Dynamic Prog. — read concepts, study static visuals, code templates
Day 343h
  • Dynamic Prog. 3h · Dynamic Prog. — read concepts, study static visuals, code templates
Day 353h
  • Dynamic Prog. 3h · Dynamic Prog. — read concepts, study static visuals, code templates

W8Week 8 · days 36–40

Day 363h
  • Dynamic Prog. 3h · Dynamic Prog. — read concepts, study static visuals, code templates
Day 373h
Day 383h
  • Tries 2h · Tries — read concepts, study static visuals, code templates · core practice: 208, 211, 212
  • Bits & Math 1h · Bits & Math — read concepts, study static visuals, code templates
Day 393h
  • Bits & Math 1h · Bits & Math — read concepts, study static visuals, code templates · core practice: 136, 191, 338
  • Mock & Review 2h · Mock & Review — read concepts, study static visuals, code templates
Day 403h
job-market context

Bangladesh interview playbook

Use DSA as one part of a broader software-interview preparation strategy.

1Typical hiring flow

Screening 60–90 min→Coding assessment→Technical interviews→HR

A commonly reported flow across Brain Station 23 and Therap BD is written/online screening (CS fundamentals + logical reasoning), then timed DSA/live coding or take-home, technical interviews and HR. A typical full timeline is about three weeks.

2What to prioritise

  • Strong DSA/problem solving is expected even for many fresher roles.
  • Language choice is usually flexible; C/C++/Java/Go/Python are commonly acceptable. Use the language in which you can reason and debug fastest.
  • Timed tests reward pattern recognition plus constraint reading. Do not force advanced techniques when a simple O(n log n) solution clearly fits.
  • Also revise OOP, DBMS, OS, networking and logical reasoning for screening rounds.

3Remote-platform angle

Toptal uses a timed Codility algorithmic test (commonly 90–120 minutes, 3–4 problems) followed by live-coding video stages. The same disciplined DSA workflow applies.

4Communities and practice

BD Programmers is problem-solving focused, and Python User Group Bangladesh is useful if Python is your interview language. Record yourself solving a problem aloud: it exposes unclear reasoning that silent practice hides.

Topic 01 · ~3h · Day 1

Python for DSA & Online Tests

Use Python deliberately: know which operations are cheap, which copy data, and how online-test input differs from LeetCode method calls.

1.1The idea

In plain English

Think of Python as a workshop. A list, dict, set, deque and heap are different tools; using the wrong one can turn a clean O(n) idea into an O(n²) program.

  • Lists are dynamic arrays: append is amortized O(1), indexing O(1), but inserting/removing near the front is O(n). Slices create copies proportional to the slice length.
  • Strings are immutable. Build many pieces in a list and use "".join(parts) instead of repeated concatenation in a long loop.
  • dict/set provide average O(1) membership; keys must be hashable. Counter and defaultdict remove bookkeeping noise.
  • Use enumerate, zip, sorted(key=...), any/all, min/max with key, and tuple ordering. heapq and bisect are standard interview tools.
  • Python floor division and modulo follow floor semantics for negatives: -3//2 == -2 and -3%2 == 1.
  • Never write [[0]*m]*n for an editable matrix: every row points at the same inner list. Avoid mutable default arguments such as def f(a=[]).
  • For deep recursion, an iterative stack is often safer; sys.setrecursionlimit can help but does not change algorithmic space.
  • LeetCode usually calls class Solution methods; online judges often require stdin parsing, multiple test cases and fast output.

1.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

from collections import Counter, defaultdict, deque
import heapq, bisect

# fast-ish stdin pattern
import sys
it = iter(sys.stdin.buffer.read().split())
n = int(next(it))
nums = [int(next(it)) for _ in range(n)]

# independent matrix rows
grid = [[0] * 4 for _ in range(3)]

# stable sort by two keys
rows.sort(key=lambda r: (r[0], -r[1]))

1.3Pattern templates

Frequency mapO(n)

Signals: counting · duplicates · most common

from collections import Counter
freq = Counter(nums)
answer = max(freq, key=freq.get)
Group by keyO(n)

Signals: group records · anagrams · buckets

from collections import defaultdict
groups = defaultdict(list)
for item in items:
    groups[key(item)].append(item)
Online-test loopO(total input)

Signals: stdin · multiple tests

import sys
input = sys.stdin.readline
for _ in range(int(input())):
    n = int(input())
    a = list(map(int, input().split()))
    print(solve(a))

1.4Visual guide

Python objects: references matterread left → right

Correct 2D list

[0,0,0]→[0,0,0]→[0,0,0]

Aliased rows

row A→same A→same A

Use a list comprehension for independent rows. Python assignment binds names to objects; it does not copy nested objects.

1.5Worked example: from brute force to the pattern

Brute-force instinct

Creating a matrix with [[0]*m]*n looks compact but repeats the same row reference. A write to one row appears in every row. Use a comprehension so each row is allocated separately.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "I want independent mutable rows, so I allocate each row inside the comprehension; repetition would alias the same object."

1.6Complexity cheat table

Operation / patternTimeSpace / note
list appendamortized O(1)dynamic-array growth
list insert/pop(0)O(n)shifts elements
dict/set lookupaverage O(1)hashing
slice a[l:r]O(k)copies k items
sortedO(n log n)Timsort, stable

1.7Mistakes that cost points

Watch out
  • Assuming slicing is O(1).
  • Using list.pop(0) as a queue instead of deque.popleft().
  • Mutating a list while iterating over it without a deliberate index strategy.
  • Shadowing built-ins like list, str, sum or input.
  • Forgetting that heapq is a min-heap.

1.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

1.9Practice set

0/6 solved4 core · 2 extra
#ProblemLevelKey idea
1Two Sum coreEasydict value → index; check complement before insert
217Contains Duplicate coreEasylen(set(nums)) or a seen set
242Valid Anagram coreEasyCompare Counter/frequency maps
49Group Anagrams coreMediumGroup by a canonical key such as 26 counts
347Top K Frequent ElementsMediumCounter + heap/buckets
128Longest Consecutive SequenceMediumStart only from numbers whose predecessor is absent
Topic 02 · ~3h · Day 2

Big-O & Complexity

Complexity is how you reject a solution before it times out. Learn to estimate time, space, amortized cost and recursion growth from constraints.

2.1The idea

In plain English

Imagine the input doubles. If the work doubles, that is linear. If it quadruples, think quadratic. If it adds one extra step, think logarithmic.

  • Ignore constant factors for asymptotic analysis, but do not ignore nested input dimensions: O(rows*cols) is not O(n) unless n means all cells.
  • Consecutive loops add; nested dependent loops often multiply. Two pointers can look nested yet remain O(n) when each pointer only moves forward.
  • Amortized analysis explains dynamic-array append: an occasional resize is expensive, but spread over many appends the average is O(1).
  • Space includes auxiliary arrays, hash maps, recursion depth and sometimes the output if the interviewer asks for total memory.
  • A recursion tree multiplies branching factor across depth. Naive Fibonacci is exponential because the same subproblems are recomputed.
  • Constraint heuristics: n≈20 may allow 2^n; n≈10³ may allow n²; n≈10⁵ usually wants n log n or n; n≈10⁶ often wants close to linear.

2.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

# Count actual operations to build intuition
ops = 0
seen = set()
for x in nums:
    ops += 1
    if x in seen:
        return True
    seen.add(x)
return False

2.3Pattern templates

Linear scanO(n)

Signals: single pass · all items

for x in nums:
    use(x)
HalvingO(log n)

Signals: sorted search · divide by 2

while n > 1:
    n //= 2
Quadratic pairsO(n²)

Signals: all pairs · matrix relation

for i in range(n):
    for j in range(i + 1, n):
        check(i, j)

2.4Visual guide

Growth rates at a glanceread left → right
O(1)
constant
O(log n)
excellent
O(n)
usually safe
O(n log n)
sorting scale
O(n²)
small n
O(2ⁿ)
tiny n only

Constraints decide the target complexity before you code. Around 10⁵ inputs usually demands near-linear or n log n work, not quadratic work.

2.5Worked example: from brute force to the pattern

Brute-force instinct

A double loop compares every pair in O(n²). A set stores previously seen values, changing membership checks to average O(1) and the full algorithm to O(n).

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "The constraint rules out pairwise comparison, so I trade O(n) extra memory for an O(n) seen-set scan."

2.6Complexity cheat table

Operation / patternTimeSpace / note
hash scanO(n)O(n)
sort + scanO(n log n)O(1)–O(n) implementation-dependent
binary searchO(log n)O(1)
naive FibonacciO(2^n)O(n) stack
memo FibonacciO(n)O(n)

2.7Mistakes that cost points

Watch out
  • Calling a loop O(n) when its body performs slicing/sorting.
  • Forgetting recursion stack space.
  • Multiplying consecutive loops instead of adding them.
  • Claiming hash lookup is guaranteed O(1); average O(1) is the interview-safe phrasing.

2.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

2.9Practice set

0/5 solved3 core · 2 extra
#ProblemLevelKey idea
704Binary Search coreEasyHalve the search interval
69Sqrt(x) coreEasyBinary search the integer answer
509Fibonacci Number coreEasyCompare exponential recursion with memo/iteration
35Search Insert PositionEasyLower-bound style binary search
278First Bad VersionEasyFirst true in a monotonic predicate
Topic 03 · ~9h · Day 3–5

Arrays, Strings & Prefix Sums

Master the default interview container: traversal, in-place mutation, prefix techniques, Kadane, matrix transforms and string counting.

3.1The idea

In plain English

An array is a row of labeled lockers. Indexing jumps directly to a locker; insertion in the middle forces everything after it to shift.

  • Dynamic arrays give O(1) indexing and amortized O(1) append. In-place edits use read/write pointers or swaps to avoid extra arrays.
  • Prefix sums store cumulative totals: range(l,r)=pref[r+1]-pref[l]. 2D prefix sums answer rectangle queries with inclusion–exclusion. Difference arrays invert the idea for many range updates.
  • Kadane keeps the best subarray ending here; if the previous sum hurts the next element, restart. Prefix/suffix products solve product-except-self without division.
  • Boyer–Moore majority voting cancels different values and leaves a candidate; if the problem does not guarantee a majority, verify it afterward.
  • Matrix patterns: transpose + reverse for rotation, boundary shrink for spiral, marker rows/columns for set-zeroes.
  • String interview patterns are often counting/normalization: anagrams, reverse words, longest common prefix, character frequencies and two pointers.

3.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

# 1D prefix sums
pref = [0]
for x in nums:
    pref.append(pref[-1] + x)

def range_sum(l, r):
    return pref[r + 1] - pref[l]

# Kadane
best = cur = nums[0]
for x in nums[1:]:
    cur = max(x, cur + x)
    best = max(best, cur)

3.3Pattern templates

Prefix sumO(n) build, O(1) query

Signals: many range sums · cumulative

pref = [0]
for x in nums: pref.append(pref[-1] + x)
ans = pref[r+1] - pref[l]
KadaneO(n)

Signals: maximum contiguous sum

cur = best = nums[0]
for x in nums[1:]:
    cur = max(x, cur + x)
    best = max(best, cur)
Prefix/suffix productO(n)

Signals: all-except-self · no division

out = [1] * n
p = 1
for i in range(n):
    out[i] = p; p *= nums[i]
s = 1
for i in range(n-1, -1, -1):
    out[i] *= s; s *= nums[i]
Matrix boundary walkO(mn)

Signals: spiral · layers

top,left,bottom,right = 0,0,m-1,n-1
while top <= bottom and left <= right:
    # traverse top/right/bottom/left, then shrink

3.4Visual guide

Prefix sums turn a range into subtractionread left → right
301142135495
prefix[l]prefix[r+1]
sum[1:5]→prefix[5] - prefix[1]→11

Precompute cumulative totals once; each later range-sum query becomes O(1). The same idea extends to 2D rectangles and difference arrays.

3.5Worked example: from brute force to the pattern

Brute-force instinct

Enumerating every subarray and summing it is O(n³), or O(n²) with prefix sums. Kadane observes that only the best sum ending at i-1 matters for extending into i, reducing the state to one number and the time to O(n).

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "At each index I either start a new subarray here or extend the best one ending immediately before it; that local recurrence is sufficient."

3.6Complexity cheat table

Operation / patternTimeSpace / note
index read/writeO(1)O(1)
appendamortized O(1)O(1)
insert/delete middleO(n)O(1)
prefix buildO(n)O(n)
KadaneO(n)O(1)
matrix traversalO(mn)O(1) extra possible

3.7Mistakes that cost points

Watch out
  • Off-by-one errors in pref[r+1]-pref[l].
  • Using 0 as Kadane initial best when all numbers can be negative.
  • Rotating a matrix in place without checking it is square.
  • Joining strings repeatedly in a loop instead of collecting pieces.

3.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

3.9Practice set

0/14 solved12 core · 2 extra
#ProblemLevelKey idea
121Best Time to Buy and Sell Stock coreEasyTrack minimum price so far and best profit
53Maximum Subarray coreMediumKadane: best ending here
238Product of Array Except Self coreMediumPrefix product then suffix product
169Majority Element coreEasyBoyer–Moore candidate cancellation
303Range Sum Query - Immutable coreEasyPrefix sum with leading zero
304Range Sum Query 2D - Immutable coreMedium2D prefix + inclusion exclusion
54Spiral Matrix coreMediumShrink four boundaries
48Rotate Image coreMediumTranspose then reverse each row
73Set Matrix Zeroes coreMediumUse first row/column as markers
189Rotate Array coreMediumReverse whole + segments or cyclic replacement
14Longest Common Prefix coreEasyCompare columns or shrink prefix
151Reverse Words in a String coreMediumsplit/filter/reverse/join or in-place language-specific
918Maximum Sum Circular SubarrayMediummax normal vs total - min subarray
41First Missing PositiveHardPlace value x at index x-1 in place
Topic 04 · ~6h · Day 6–7

Hashing: dict, set & Counter

Hashing converts repeated search into direct lookup. Learn complements, frequencies, grouping, prefix-count maps and O(1) design patterns.

4.1The idea

In plain English

A hash table is a wall of mailboxes. The hash function chooses the mailbox; collisions mean several letters share a box, so the key is still checked.

  • dict maps keys to values; set stores only keys. Average lookup/insert/delete is O(1), with O(n) worst case theoretically.
  • Hashable keys must have stable hashes: integers, strings and tuples of hashables work; lists and dicts do not.
  • Complement lookup solves Two Sum: for x, ask whether target-x was seen. Frequency maps power anagrams, top-k and counting.
  • Group-by problems need a canonical key: sorted string O(k log k), or a 26-count tuple O(k) for lowercase letters.
  • Prefix-sum + hashmap counts subarrays: if current prefix is p, a previous p-k creates a sum-k subarray.
  • Last-seen index maps support longest substring/window problems; sets support consecutive-sequence expansion.
  • O(1) design questions often combine structures, e.g. a hash map plus array or doubly linked list.

4.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

from collections import Counter, defaultdict

# prefix sum + count map
count = {0: 1}
prefix = ans = 0
for x in nums:
    prefix += x
    ans += count.get(prefix - k, 0)
    count[prefix] = count.get(prefix, 0) + 1

4.3Pattern templates

Complement lookupO(n)

Signals: pair sum · lookup missing value

seen = {}
for i, x in enumerate(nums):
    if target - x in seen:
        return [seen[target-x], i]
    seen[x] = i
Frequency mapO(n)

Signals: counts · anagram · top-k

from collections import Counter
freq = Counter(items)
Prefix + hashmapO(n)

Signals: subarray sum = k · count ranges

freq = {0:1}; p=ans=0
for x in nums:
    p += x
    ans += freq.get(p-k, 0)
    freq[p] = freq.get(p,0)+1

4.4Visual guide

Hash table: key → bucket → entryread left → right
0("cat", 2)1—2("tea", 1) → ("ate", 1)3("dog", 3)

Average O(1) lookup comes from hashing a key to a bucket. Collisions are normal; equality checks inside a bucket resolve them.

4.5Worked example: from brute force to the pattern

Brute-force instinct

A brute-force O(n²) enumeration can compute every range. Using prefix sums, a subarray ending now sums to k exactly when an earlier prefix equals current-k; a frequency map counts all such earlier prefixes in O(1) average time.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "I store how many times each prefix sum occurred. For current prefix p, every previous p-k creates one valid subarray ending here."

4.6Complexity cheat table

Operation / patternTimeSpace / note
lookup/insert/deleteaverage O(1)O(n)
frequency buildO(n)O(n)
group anagramsO(total chars) with count keyO(total chars)
longest consecutiveO(n) averageO(n)
prefix-hash subarraysO(n)O(n)

4.7Mistakes that cost points

Watch out
  • Using an unhashable list as a dict key; convert to tuple.
  • In Two Sum, inserting before checking can accidentally reuse the same element in some formulations.
  • For longest consecutive, expanding from every number creates O(n²); only start when x-1 is absent.
  • For prefix counting, forgetting the initial {0:1} loses subarrays starting at index 0.

4.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

4.9Practice set

0/11 solved8 core · 3 extra
#ProblemLevelKey idea
1Two Sum coreEasyComplement lookup in a dict
217Contains Duplicate coreEasySeen set
242Valid Anagram coreEasyCounter or fixed 26-count array
49Group Anagrams coreMediumCanonical count tuple as key
128Longest Consecutive Sequence coreMediumExpand only from sequence starts
560Subarray Sum Equals K coreMediumPrefix-sum frequency map
219Contains Duplicate II coreEasyLast index map or bounded window set
36Valid Sudoku coreMediumSets for row, column and box constraints
705Design HashSetEasyBuckets + collision handling
706Design HashMapEasyBuckets storing key/value pairs
380Insert Delete GetRandom O(1)MediumArray + value→index map
Topic 05 · ~5h · Day 8–9

Two Pointers

Two indices walking the same data turn many pair and in-place problems from O(n²) into one linear scan.

5.1The idea

In plain English

Put one finger on each end of a sorted row. If the sum is too small move the left finger to a larger value; if too large move the right finger to a smaller value.

  • Opposite-end pointers exploit sorted order for pair sums, palindrome checks, water/container problems and shrinking intervals.
  • Same-direction read/write pointers compact arrays in place and remove duplicates without repeated shifting.
  • Two-sequence pointers merge sorted arrays or test subsequences in O(n+m).
  • For 3Sum, sort, fix one value, then run a two-pointer pair scan on the suffix. Skip duplicates deliberately.
  • The proof of O(n) is that each pointer moves in one direction at most n times.

5.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

def pair_sum_sorted(nums, target):
    l, r = 0, len(nums)-1
    while l < r:
        s = nums[l] + nums[r]
        if s == target: return [l, r]
        if s < target: l += 1
        else: r -= 1
    return []

5.3Pattern templates

Opposite endsO(n)

Signals: sorted pair · palindrome · container

l, r = 0, len(a)-1
while l < r:
    if need_bigger: l += 1
    else: r -= 1
Read/writeO(n)

Signals: in place · remove/keep · compact

w = 0
for r in range(len(a)):
    if keep(a[r]):
        a[w] = a[r]; w += 1
Sort + fixed + two pointersO(n²)

Signals: 3Sum · triplets

nums.sort()
for i in range(n-2):
    l, r = i+1, n-1
    while l < r: ...

5.4Visual guide

Opposite ends eliminate impossible pairsread left → right
1031426384115146157
L moves right when sum is smallR moves left when sum is large

On sorted data, one comparison can discard an entire set of pairs. Each pointer moves at most n times, giving O(n).

5.5Worked example: from brute force to the pattern

Brute-force instinct

Brute force checks every triplet in O(n³). Sorting allows each fixed first value to reduce the remainder to sorted Two Sum in O(n), producing O(n²) overall. Duplicate skipping avoids repeated answers.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "Sorting costs n log n, but the dominant cost is n linear two-pointer scans, so O(n²). Sorting also lets me eliminate duplicate triplets by comparing neighbours."

5.6Complexity cheat table

Operation / patternTimeSpace / note
sorted pair sumO(n)O(1)
3SumO(n²)O(1) extra apart from sort/output
valid palindromeO(n)O(1)
in-place compactionO(n)O(1)
merge sorted sequencesO(n+m)O(n+m) output

5.7Mistakes that cost points

Watch out
  • Using opposite-end logic on unsorted data.
  • Forgetting that l
  • Failing to move a pointer in every loop branch.
  • Sorting when the problem requires original indices without preserving them.

5.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

5.9Practice set

0/12 solved7 core · 5 extra
#ProblemLevelKey idea
125Valid Palindrome coreEasySkip non-alphanumerics from both ends
344Reverse String coreEasySwap ends inward
26Remove Duplicates from Sorted Array coreEasyRead/write pointer
283Move Zeroes coreEasyCompact nonzero values
977Squares of a Sorted Array coreEasyFill result from largest absolute end
167Two Sum II - Input Array Is Sorted coreMediumMove left/right by comparison
153Sum coreMediumSort + fixed index + pair scan
11Container With Most WaterMediumMove the shorter wall
392Is SubsequenceEasyTwo sequence pointers
88Merge Sorted ArrayEasyFill from the back
680Valid Palindrome IIEasyTry one skip after first mismatch
42Trapping Rain WaterHardTwo sides with running maxima
Topic 06 · ~6h · Day 9–11

Sliding Window

Sliding windows solve contiguous-subarray and substring problems by maintaining exactly the information inside a moving range.

6.1The idea

In plain English

Imagine a camera frame sliding over a long banner. Instead of recounting everything visible after each move, subtract what leaves and add what enters.

  • Fixed windows keep exact length k: initialize the first window, then add right and remove left on each slide.
  • Variable windows expand right until a rule breaks, then shrink left until valid again. Each index enters/leaves once, so O(n).
  • Longest valid uses expand/shrink while tracking best length. Shortest satisfying often shrinks aggressively after reaching the target.
  • At-most-K counting lets you derive exactly-K counts as atMost(K)-atMost(K-1).
  • Negative numbers break many sum-window monotonicity assumptions; prefix sums or monotonic deque techniques are then safer.
  • Sliding maximum uses a decreasing deque of indices, removing expired indices from the front and dominated values from the back.

6.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

from collections import deque

# variable window: longest without repeats
seen = set(); l = best = 0
for r, ch in enumerate(s):
    while ch in seen:
        seen.remove(s[l]); l += 1
    seen.add(ch)
    best = max(best, r-l+1)

6.3Pattern templates

Fixed windowO(n)

Signals: exact k · moving average/sum

cur = sum(a[:k]); best = cur
for r in range(k, len(a)):
    cur += a[r] - a[r-k]
    best = max(best, cur)
Longest validO(n)

Signals: longest substring/subarray under rule

l = 0
for r, x in enumerate(a):
    add(x)
    while invalid():
        remove(a[l]); l += 1
    best = max(best, r-l+1)
Monotonic dequeO(n)

Signals: window max/min

dq = deque()
for i, x in enumerate(a):
    while dq and a[dq[-1]] <= x: dq.pop()
    dq.append(i)
    if dq[0] <= i-k: dq.popleft()

6.4Visual guide

A window keeps just the state you needread left → right
a0b1c2a3b4c5b6b7
leftright

Expand right to gain information; shrink left only when the window violates the rule. Every index enters and leaves at most once, so variable windows are usually O(n).

6.5Worked example: from brute force to the pattern

Brute-force instinct

Checking every substring and uniqueness is O(n²) or worse. A set plus left pointer maintains a duplicate-free window; when s[r] repeats, remove from the left until valid, then update the best length.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "The window invariant is that all characters are unique. Right expands once per character and left only moves forward, so total work is O(n)."

6.6Complexity cheat table

Operation / patternTimeSpace / note
fixed windowO(n)O(1) or state size
variable windowO(n)O(alphabet/state)
frequency compareO(n)O(alphabet)
sliding max dequeO(n)O(k)
brute all windowsO(nk) or O(n²)varies

6.7Mistakes that cost points

Watch out
  • Using a sum-based shrinking window when numbers may be negative.
  • Removing the wrong frequency or forgetting to delete zero-count keys when comparing maps.
  • For deques, storing values instead of indices when expiration matters.
  • Shrinking only once with if when the invariant may require a while loop.

6.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

6.9Practice set

0/10 solved8 core · 2 extra
#ProblemLevelKey idea
643Maximum Average Subarray I coreEasyFixed k running sum
3Longest Substring Without Repeating Characters coreMediumVariable window + set/last index
209Minimum Size Subarray Sum coreMediumShrink after sum reaches target
424Longest Repeating Character Replacement coreMediumWindow size - max frequency <= k
567Permutation in String coreMediumFixed window frequency equality
1004Max Consecutive Ones III coreMediumAt most k zeros
76Minimum Window Substring coreHardNeed/have counts; shrink while satisfied
239Sliding Window Maximum coreHardDecreasing deque of indices
438Find All Anagrams in a StringMediumFixed window frequency map
1456Maximum Number of Vowels in a Substring of Given LengthMediumFixed k count
Topic 07 · ~5h · Day 11–13

Stacks & Monotonic Stack

Stacks solve nested structure, undo/evaluation and next-greater problems; monotonic stacks turn many quadratic scans into amortized linear time.

7.1The idea

In plain English

A stack is a pile of plates: only the top is directly accessible. That LIFO order exactly matches nested brackets and unfinished work.

  • Bracket validation pushes opening symbols and matches closing symbols against the top.
  • MinStack stores extra information per push (or a second stack) so minimum remains O(1).
  • RPN evaluation pushes operands and pops the last two for each operator; decode-string uses stacks for nested repetition.
  • A monotonic stack keeps values or indices increasing/decreasing. A new value pops candidates it resolves.
  • Daily Temperatures, next greater/smaller, stock span and histogram area share this unresolved-candidate pattern.
  • Largest rectangle uses increasing bar indices; when a lower bar arrives, popped height determines an area whose right boundary is now known.

7.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

# next greater index to the right
stack = []
ans = [-1] * len(nums)
for i, x in enumerate(nums):
    while stack and nums[stack[-1]] < x:
        j = stack.pop()
        ans[j] = i
    stack.append(i)

7.3Pattern templates

Bracket stackO(n)

Signals: nested delimiters · matching

pairs = {')':'(',']':'[','}':'{'}
st=[]
for ch in s:
    if ch in pairs.values(): st.append(ch)
    elif not st or st.pop()!=pairs[ch]: return False
Monotonic stackO(n)

Signals: next greater/smaller · span

st=[]
for i,x in enumerate(a):
    while st and a[st[-1]] < x:
        j=st.pop(); answer[j]=i
    st.append(i)
HistogramO(n)

Signals: largest rectangle · nearest smaller

st=[]
for i,h in enumerate(heights+[0]):
    while st and heights[st[-1]] > h:
        j=st.pop(); left=st[-1] if st else -1
        best=max(best, heights[j]*(i-left-1))
    st.append(i)

7.4Visual guide

Monotonic stack keeps unresolved candidatesread left → right

temperatures

730741752713694725766737

stack of indices

4 : 693 : 712 : 75

When a new value resolves older candidates, pop them. Each index is pushed and popped at most once: amortized O(n).

7.5Worked example: from brute force to the pattern

Brute-force instinct

A brute scan looks right from every day, O(n²). A decreasing stack keeps only days still waiting for a warmer temperature. Each day is pushed once and popped once, making O(n).

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "The stack contains unresolved indices in decreasing temperature order. Today resolves every colder day on top."

7.6Complexity cheat table

Operation / patternTimeSpace / note
push/pop/topO(1)O(n)
valid parenthesesO(n)O(n)
RPNO(n)O(n)
monotonic next greaterO(n) amortizedO(n)
largest rectangleO(n)O(n)

7.7Mistakes that cost points

Watch out
  • Popping operands in the wrong order for subtraction/division in RPN.
  • Storing values when you need distances; store indices.
  • Using < vs <= incorrectly with equal values in monotonic stacks.
  • For histogram, forgetting a sentinel zero or final cleanup pass.

7.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

7.9Practice set

0/10 solved6 core · 4 extra
#ProblemLevelKey idea
20Valid Parentheses coreEasyMatch closers to stack top
155Min Stack coreMediumStore running minimum per push
150Evaluate Reverse Polish Notation coreMediumStack operands; preserve operand order
739Daily Temperatures coreMediumDecreasing stack of unresolved indices
496Next Greater Element I coreEasyMonotonic stack + map result
84Largest Rectangle in Histogram coreHardIncreasing stack; pop on lower height
394Decode StringMediumStack nested count/string state
735Asteroid CollisionMediumResolve collisions against stack top
901Online Stock SpanMediumDecreasing stack of price/span pairs
71Simplify PathMediumStack path components
Topic 08 · ~2h · Day 13

Queues & Deques

Queues model first-in-first-out work. In Python, deque is the right primitive for BFS, rolling time windows and double-ended updates.

8.1The idea

In plain English

A checkout line serves the oldest waiting person first. A deque adds a second door, so items can efficiently enter or leave from either end.

  • collections.deque supports append, appendleft, pop and popleft in O(1). list.pop(0) is O(n) because elements shift.
  • A circular queue stores front index and size in a fixed array, wrapping with modulo arithmetic.
  • A queue via two stacks uses one stack for incoming items and one for outgoing; transfer only when outgoing is empty for amortized O(1).
  • Recent-calls problems push the newest timestamp and popleft expired timestamps.
  • deque.rotate can express circular simulation such as Josephus, though arithmetic solutions may be faster.
  • BFS is queue-driven: mark a node when enqueued to prevent duplicates.

8.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

from collections import deque
q = deque([1, 2, 3])
q.append(4)       # back
x = q.popleft()   # front

8.3Pattern templates

FIFO queueO(1) operations

Signals: BFS · tasks · stream

from collections import deque
q=deque([start])
while q:
    node=q.popleft()
    for nei in graph[node]: ...
Circular queueO(1)

Signals: fixed capacity · wraparound

tail = (head + size) % cap
buf[tail] = value
size += 1
Two stacksamortized O(1)

Signals: implement queue using stacks

if not out:
    while inp: out.append(inp.pop())
return out.pop()

8.4Visual guide

Queue = first in, first outread left → right
front →task Atask Btask Ctask D← back

Use collections.deque for O(1) append and popleft. A Python list with pop(0) shifts remaining elements and costs O(n).

8.5Worked example: from brute force to the pattern

Brute-force instinct

Keeping every timestamp and rescanning is unnecessary. Because timestamps arrive in order, append the new time and remove old times from the front until the first is within the window.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "The deque contains exactly the active time interval; each timestamp enters and leaves once, so calls are amortized O(1)."

8.6Complexity cheat table

Operation / patternTimeSpace / note
deque append/popleftO(1)O(n)
list pop(0)O(n)O(n)
circular queue enqueue/dequeueO(1)O(capacity)
two-stack queueamortized O(1)O(n)

8.7Mistakes that cost points

Watch out
  • Using list.pop(0) in BFS.
  • For circular queues, confusing capacity, size and head/tail indices.
  • Marking BFS visited on dequeue rather than enqueue.

8.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

8.9Practice set

0/5 solved3 core · 2 extra
#ProblemLevelKey idea
232Implement Queue using Stacks coreEasyLazy transfer between input/output stacks
933Number of Recent Calls coreEasyDeque timestamps; evict older than t-3000
622Design Circular Queue coreMediumFixed array + head + size + modulo
1823Find the Winner of the Circular GameMediumQueue/deque simulation or Josephus recurrence
649Dota2 SenateMediumTwo queues of future indices
Topic 09 · ~6h · Day 14–15

Linked Lists

Linked lists test pointer discipline: dummy heads, reversal, fast/slow detection, merging, reordering and hash-map + doubly-linked-list design.

9.1The idea

In plain English

Think of train cars connected by couplers. To reorder the train you change links between cars; there is no direct jump to the 50th car.

  • A dummy head removes special cases when the real head can change. Return dummy.next.
  • Iterative reversal stores next before overwriting curr.next. Recursive reversal returns the new head then flips one edge.
  • Fast/slow pointers find the middle and detect cycles. Floyd cycle-start proof resets one pointer to head after the first meeting.
  • Gap pointers remove nth-from-end: move fast n steps ahead, then advance both until fast reaches the end.
  • Merge two sorted lists by attaching the smaller current node to a tail pointer.
  • Reorder list = find middle, reverse second half, alternate merge. Palindrome = compare first half against reversed second half.
  • LRU Cache combines O(1) hashmap lookup with a doubly linked list that moves recently used nodes to one end.

9.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

def reverse(head):
    prev, cur = None, head
    while cur:
        nxt = cur.next
        cur.next = prev
        prev, cur = cur, nxt
    return prev

9.3Pattern templates

Dummy headO(n)

Signals: head may change · merge/delete

dummy = ListNode(0, head)
prev = dummy
# mutate through prev.next
return dummy.next
Fast / slowO(n)

Signals: middle · cycle · palindrome

slow = fast = head
while fast and fast.next:
    slow = slow.next
    fast = fast.next.next
Gap pointersO(n)

Signals: nth from end

dummy=ListNode(0,head); fast=slow=dummy
for _ in range(n+1): fast=fast.next
while fast: fast=fast.next; slow=slow.next
slow.next=slow.next.next

9.4Visual guide

Reverse a linked list by rewiring arrowsread left → right
None→1→2→3→4

prev ← curr → next becomes prev ← curr, then advance.

A dummy node simplifies head-edge cases. Fast/slow pointers solve middle, cycle and palindrome patterns without extra arrays.

9.5Worked example: from brute force to the pattern

Brute-force instinct

Copying values into an array and rebuilding uses O(n) extra space. Rewiring next pointers needs only prev/curr/next and O(1) extra space.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "Before changing curr.next I save the original next node; then I reverse one edge and advance both pointers."

9.6Complexity cheat table

Operation / patternTimeSpace / note
access kth nodeO(k)O(1)
reverseO(n)O(1)
cycle detectionO(n)O(1)
merge sortedO(n+m)O(1)
LRU get/putO(1)O(capacity)

9.7Mistakes that cost points

Watch out
  • Losing the remainder of the list by overwriting curr.next before saving nxt.
  • For remove-nth, off-by-one mistakes in the initial gap; a dummy node makes this safer.
  • In cycle problems, comparing node values instead of node identity.
  • For palindrome, forgetting whether restoring the list is required by the environment.

9.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

9.9Practice set

0/12 solved8 core · 4 extra
#ProblemLevelKey idea
206Reverse Linked List coreEasyprev/curr/nxt rewiring
21Merge Two Sorted Lists coreEasyDummy head + tail
141Linked List Cycle coreEasyFloyd slow/fast meeting
142Linked List Cycle II coreMediumReset one pointer to head after meeting
19Remove Nth Node From End of List coreMediumDummy + n+1 gap
234Palindrome Linked List coreEasyReverse second half and compare
160Intersection of Two Linked Lists coreEasySwitch heads to equalize path lengths
143Reorder List coreMediumMiddle + reverse + weave
2Add Two NumbersMediumDigit-wise carry with dummy tail
138Copy List with Random PointerMediumOld→new map or interweaving nodes
146LRU CacheMediumDict + doubly linked list
25Reverse Nodes in k-GroupHardReverse each full k-block
Topic 10 · ~4h · Day 16–17

Recursion

Recursion makes tree-shaped reasoning concise. Learn base cases, call-stack cost, memoization, fast exponentiation and how to convert recursion to an explicit stack.

10.1The idea

In plain English

A recursive function delegates a smaller version of the same job to a future copy of itself, then finishes its own work when that copy returns.

  • Every recursion needs progress toward a base case. The "leap of faith" is to assume the smaller call works, then combine.
  • The call stack stores local variables and return locations; maximum recursion depth therefore contributes to space complexity.
  • Naive Fibonacci repeats subproblems; functools.cache converts the recursion tree into a DAG of unique states.
  • Fast power halves the exponent: x^n = (x^(n//2))², with one extra x for odd n, giving O(log n).
  • Avoid recursive slicing like solve(a[1:]) when indexes can avoid O(n) copies.
  • Any DFS recursion can usually be converted to an explicit stack that stores the same state manually.

10.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

from functools import cache

@cache
def fib(n):
    if n < 2: return n
    return fib(n-1) + fib(n-2)

def power(x, n):
    if n == 0: return 1
    half = power(x, n//2)
    return half*half if n%2==0 else x*half*half

10.3Pattern templates

Divide recursivelyO(log n)

Signals: halve input · exponentiation

def power(x,n):
    if n==0: return 1
    h=power(x,n//2)
    return h*h if n%2==0 else x*h*h
Memo recursionO(states)

Signals: overlapping subproblems

from functools import cache
@cache
def f(state):
    if base(state): return base_value
    return combine(f(next_state))
Explicit stacksame asymptotic DFS

Signals: avoid recursion depth

stack=[start]
while stack:
    state=stack.pop()
    # push children in reverse desired order

10.4Visual guide

Recursion is a stack of smaller promisesread left → right
fib(5)
fib(4)fib(3)
memofib(2)memo

Define the base case, trust the smaller call, and combine its result. Memoization collapses repeated subtrees.

10.5Worked example: from brute force to the pattern

Brute-force instinct

Naive recursion branches twice and recomputes the same fib(k) many times, causing exponential work. Cache each n so every state is solved once: O(n) time and O(n) memory.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "The recurrence is fine; the problem is repeated states. Memoization stores each fib(n) once, collapsing exponential recomputation to linear."

10.6Complexity cheat table

Operation / patternTimeSpace / note
factorial recursionO(n)O(n) stack
naive FibonacciO(2^n)O(n) stack
memo FibonacciO(n)O(n)
fast powerO(log n)O(log n) stack
iterative DFS equivalentO(V+E)O(V)

10.7Mistakes that cost points

Watch out
  • Missing or unreachable base cases.
  • Mutating one shared object across recursive branches without undoing changes.
  • Ignoring Python recursion depth on long chains/trees.
  • Using slicing at every recursive call and accidentally increasing time/space.

10.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

10.9Practice set

0/5 solved4 core · 1 extra
#ProblemLevelKey idea
509Fibonacci Number coreEasyCompare naive recursion, cache and iteration
50Pow(x, n) coreMediumExponentiation by squaring
344Reverse String coreEasyRecursive swap from ends inward
24Swap Nodes in Pairs coreMediumRecursively solve rest then swap first pair
779K-th Symbol in GrammarMediumRecurse by parent relation/parity
Topic 11 · ~6h · Day 17–19

Backtracking

Backtracking enumerates a decision tree while reusing one mutable path. Learn subsets, permutations, combinations, pruning, grids and N-Queens.

11.1The idea

In plain English

You are exploring a maze with a pencil. At each junction choose a route, mark it, explore, then erase that choice when you return to try another route.

  • The universal template is choose → explore → unchoose. Save path[:] or tuple(path) because path itself keeps changing.
  • Subsets branch include/exclude; permutations choose one unused item; combinations advance a start index to avoid order duplicates.
  • Combination Sum prunes when remaining target is negative; duplicate variants usually sort first and skip equal siblings.
  • Generate Parentheses tracks open/close counts and prunes invalid prefixes instead of generating all 2^(2n) strings.
  • Word Search marks a grid cell during the path and restores it after recursion. N-Queens tracks occupied columns and diagonals in sets.
  • Complexity often follows output size: 2^n subsets, n! permutations. Pruning improves practical work but does not magically change worst case in many problems.

11.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

def subsets(nums):
    out, path = [], []
    def dfs(i):
        if i == len(nums):
            out.append(path[:]); return
        path.append(nums[i]); dfs(i+1); path.pop()
        dfs(i+1)
    dfs(0)
    return out

11.3Pattern templates

SubsetsO(n·2^n)

Signals: include/exclude · all subsets

def dfs(i):
    if i==n: out.append(path[:]); return
    path.append(a[i]); dfs(i+1); path.pop()
    dfs(i+1)
PermutationsO(n·n!)

Signals: arrangements · use each once

def dfs():
    if len(path)==n: out.append(path[:]); return
    for x in nums:
        if x in used: continue
        used.add(x); path.append(x)
        dfs()
        path.pop(); used.remove(x)
Combination with pruningdepends on tree

Signals: target sum · sorted choices

for i in range(start,n):
    if i>start and a[i]==a[i-1]: continue
    if a[i] > remain: break
    path.append(a[i]); dfs(i+1, remain-a[i]); path.pop()

11.4Visual guide

Choose → explore → unchooseread left → right
[]
[1][]
[1,2][1][2][]

The search tree is the algorithm. Prune branches as soon as they can no longer produce a valid answer; copy the path when saving it.

11.5Worked example: from brute force to the pattern

Brute-force instinct

Generating all binary strings and filtering wastes most branches. Build only prefixes where close<=open<=n; every explored prefix can still lead to a valid answer.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "I encode validity in the recursion state, so I never recurse into a prefix with too many closing parentheses or too many opens."

11.6Complexity cheat table

Operation / patternTimeSpace / note
subsetsO(n·2^n)O(n) path + output
permutationsO(n·n!)O(n) path/used + output
N-QueensO(n!) worst-case styleO(n)
word searchO(mn·4^L)O(L) stack
parenthesesO(Catalan(n)·n)O(n) path

11.7Mistakes that cost points

Watch out
  • Appending path instead of path[:] and getting repeated final state.
  • For duplicate combinations, skipping equal values across different recursion depths incorrectly; only skip equal siblings.
  • Not restoring grid/used state on every return path.
  • Using "x in path" for permutations can fail when input contains duplicate values; track indices/used positions.

11.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

11.9Practice set

0/12 solved8 core · 4 extra
#ProblemLevelKey idea
78Subsets coreMediumInclude/exclude or for-loop backtracking
46Permutations coreMediumUsed set/array + path
77Combinations coreMediumstart index prevents reordered duplicates
39Combination Sum coreMediumReuse current candidate; prune by remaining target
90Subsets II coreMediumSort + skip duplicate siblings
22Generate Parentheses coreMediumMaintain open/close validity
79Word Search coreMediumDFS grid with temporary visited mark
51N-Queens coreHardColumns and two diagonal sets
40Combination Sum IIMediumEach candidate once + duplicate skipping
131Palindrome PartitioningMediumChoose next palindromic segment
17Letter Combinations of a Phone NumberMediumCartesian-product recursion
37Sudoku SolverHardConstraint sets + backtracking
Topic 12 · ~5h · Day 19–20

Sorting

Sorting is both a tool and a family of interview algorithms. Understand stability, keys, divide-and-conquer, partitioning and selection.

12.1The idea

In plain English

Sorting is arranging books on a shelf by a chosen label. Once ordered, many later questions become much easier: duplicates cluster, intervals line up and binary search becomes possible.

  • Python list.sort mutates and returns None; sorted returns a new list. Timsort is stable and O(n log n) worst case.
  • key functions are preferred over comparators. Tuple keys naturally express multi-column order; cmp_to_key exists for rare comparator-style rules.
  • Bubble and selection are O(n²) teaching algorithms; insertion is O(n²) worst-case but good on tiny/nearly-sorted regions.
  • Merge sort is stable O(n log n) with O(n) extra memory. Quicksort partitions around a pivot: average O(n log n), worst O(n²); random pivots reduce adversarial risk.
  • Quickselect partitions but recurses/iterates only into the side containing kth, giving O(n) average selection.
  • Counting/bucket sort beat comparison sorting when keys occupy a small known range.

12.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

# Python: stable multi-key sort
records.sort(key=lambda r: (r.department, -r.score, r.name))

# Lomuto partition skeleton
pivot = a[hi]
i = lo
for j in range(lo, hi):
    if a[j] <= pivot:
        a[i], a[j] = a[j], a[i]
        i += 1
a[i], a[hi] = a[hi], a[i]

12.3Pattern templates

Merge sortO(n log n)

Signals: stable · divide and merge

def sort(a):
    if len(a)<=1: return a
    m=len(a)//2
    return merge(sort(a[:m]), sort(a[m:]))
Quicksort partitionO(n) per partition

Signals: pivot · in-place partition

pivot=a[hi]; i=lo
for j in range(lo,hi):
    if a[j] <= pivot:
        a[i],a[j]=a[j],a[i]; i+=1
a[i],a[hi]=a[hi],a[i]
QuickselectO(n) average

Signals: kth element · partial sort

while lo <= hi:
    p=partition(a,lo,hi)
    if p==k:return a[p]
    if p<k:lo=p+1
    else:hi=p-1

12.4Visual guide

Sorting exposes order that algorithms can exploitread left → right
7
3
9
2
6
1

Stable sorting preserves the relative order of equal keys. Python sorted/list.sort use Timsort; custom algorithms still matter for interview reasoning and partition patterns.

12.5Worked example: from brute force to the pattern

Brute-force instinct

Sorting all values costs O(n log n). Quickselect converts kth-largest to an index and repeatedly partitions only the relevant side, averaging O(n) time.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "I do not need total order; partitioning tells me which side contains the target rank, so I discard the other side."

12.6Complexity cheat table

Operation / patternTimeSpace / note
Python sortO(n log n)O(n) worst auxiliary implementation detail
insertion sortO(n²)O(1)
merge sortO(n log n)O(n)
quicksortavg O(n log n), worst O(n²)O(log n) avg stack
quickselectavg O(n), worst O(n²)O(1) iterative

12.7Mistakes that cost points

Watch out
  • Writing a comparator when a key tuple is simpler and safer.
  • For quickselect, mixing kth-largest rank with zero-based kth-smallest index.
  • Quicksort duplicate-heavy inputs can degrade with poor partitioning/pivots.
  • For stability requirements, not checking whether the chosen algorithm preserves equal-key order.

12.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

12.9Practice set

0/10 solved6 core · 4 extra
#ProblemLevelKey idea
912Sort an Array coreMediumImplement merge sort or randomized quicksort
75Sort Colors coreMediumDutch national flag three-way partition
215Kth Largest Element in an Array coreMediumHeap or quickselect
148Sort List coreMediumMerge sort fits linked lists
56Merge Intervals coreMediumSort by start then merge overlaps
347Top K Frequent Elements coreMediumBucket sort by frequency or heap
451Sort Characters By FrequencyMediumFrequency buckets/sort
164Maximum GapMediumBucket/pigeonhole idea after finding range
179Largest NumberMediumCustom order comparing a+b vs b+a
973K Closest Points to OriginMediumQuickselect/heap by squared distance
Topic 13 · ~6h · Day 21–22

Binary Search

Binary search is an invariant over a monotonic search space. Master exact search, boundaries, rotated arrays and binary search on the answer.

13.1The idea

In plain English

You are guessing a number and each answer says "too low" or "too high." That feedback discards half the possibilities every time.

  • Exact search often uses [lo, hi] with while lo<=hi. Boundary search can use [lo, hi) or a first-true template; consistency matters more than the specific style.
  • bisect_left returns the insertion point before equal values; bisect_right returns after equal values.
  • First/last occurrence uses lower-bound variants. Rotated-array search identifies which half is sorted, then decides whether the target lies inside it.
  • Find minimum in a rotated array compares mid to right. Peak element uses a local slope: nums[mid] < nums[mid+1] means a peak exists to the right.
  • 2D sorted matrix problems may flatten coordinates or binary search rows then columns.
  • Binary search on answer applies when "can we achieve X?" is monotonic: Koko speed, ship capacity, minimum feasible maximum, etc.

13.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

# lower bound: first index with a[i] >= target
lo, hi = 0, len(a)
while lo < hi:
    mid = (lo + hi) // 2
    if a[mid] < target:
        lo = mid + 1
    else:
        hi = mid
return lo

13.3Pattern templates

Exact searchO(log n)

Signals: sorted · find target

lo,hi=0,n-1
while lo<=hi:
    mid=(lo+hi)//2
    if a[mid]==x:return mid
    if a[mid]<x:lo=mid+1
    else:hi=mid-1
Lower boundO(log n)

Signals: first >= x · first true

lo,hi=0,n
while lo<hi:
    mid=(lo+hi)//2
    if a[mid] < x: lo=mid+1
    else: hi=mid
return lo
Search answerO(log range · check)

Signals: minimum feasible · maximum possible

lo,hi=min_answer,max_answer
while lo<hi:
    mid=(lo+hi)//2
    if feasible(mid): hi=mid
    else: lo=mid+1
return lo

13.4Visual guide

Binary search halves the candidate rangeread left → right
205182123164235386567728
lomid = 4hi

State the invariant first. For exact search use lo ≤ hi; for first-true/lower-bound searches, keep a half-open or closed invariant consistently.

13.5Worked example: from brute force to the pattern

Brute-force instinct

Trying every speed up to max(piles) is too slow. The predicate "can finish within h hours at speed k" changes from false to true as k increases, so binary-search the first feasible speed.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "The answer space is monotonic: once a speed works, every larger speed works. I binary-search the minimum feasible speed."

13.6Complexity cheat table

Operation / patternTimeSpace / note
exact searchO(log n)O(1)
lower/upper boundO(log n)O(1)
rotated searchO(log n) typical distinct valuesO(1)
binary answerO(log R · check)depends on check
bisect insertionO(log n) locate + O(n) list insertO(1)

13.7Mistakes that cost points

Watch out
  • Mixing lo<=hi updates with a lo
  • For first-true search, returning mid instead of the converged boundary.
  • Integer overflow is not a Python issue but mid=(lo+hi)//2 is still conventional; in other languages use lo+(hi-lo)//2.
  • Assuming binary search applies without a monotonic predicate.

13.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

13.9Practice set

0/11 solved8 core · 3 extra
#ProblemLevelKey idea
704Binary Search coreEasyExact closed-interval search
35Search Insert Position coreEasyLower bound
34Find First and Last Position of Element in Sorted Array coreMediumTwo boundary searches
33Search in Rotated Sorted Array coreMediumOne half is sorted each iteration
153Find Minimum in Rotated Sorted Array coreMediumCompare mid with right boundary
162Find Peak Element coreMediumFollow uphill slope
74Search a 2D Matrix coreMediumFlattened binary search
875Koko Eating Bananas coreMediumBinary search minimum feasible speed
69Sqrt(x)EasySearch integer square root
1011Capacity To Ship Packages Within D DaysMediumBinary search minimum capacity
410Split Array Largest SumHardSearch minimal feasible maximum segment sum
Topic 14 · ~8h · Day 23–25

Binary Trees

Trees combine recursion, stacks and queues. Learn DFS/BFS, bottom-up returns, global/nonlocal answers, reconstruction and serialization.

14.1The idea

In plain English

A tree is an organization chart. To answer a question about the CEO node, you often first ask each department subtree for a summary, then combine the summaries.

  • Vocabulary: root, parent, child, leaf, depth, height, subtree. LeetCode level-order arrays use None for missing children.
  • DFS recursive traversals are preorder/inorder/postorder depending on when you process the node. Iterative versions explicitly manage a stack.
  • BFS uses a queue; processing len(queue) nodes at a time gives levels. Right-side view takes the last item of each level.
  • Bottom-up recursion returns information such as subtree height; top-down recursion carries information such as path sum/depth from ancestors.
  • Diameter and max-path-sum keep a nonlocal/global best while each call returns a value usable by its parent.
  • Same/symmetric/subtree compare structure plus values. Invert swaps children. LCA returns the node where targets split across subtrees.
  • Build from preorder+inorder uses preorder for root order and an inorder-index map for subtree boundaries. Serialization needs an unambiguous null marker.

14.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

from collections import deque

def level_order(root):
    if not root: return []
    q = deque([root]); out = []
    while q:
        level = []
        for _ in range(len(q)):
            node = q.popleft(); level.append(node.val)
            if node.left: q.append(node.left)
            if node.right: q.append(node.right)
        out.append(level)
    return out

14.3Pattern templates

Bottom-up DFSO(n)

Signals: height · balance · diameter

def dfs(node):
    if not node:return 0
    L=dfs(node.left); R=dfs(node.right)
    update_answer(L,R,node)
    return 1+max(L,R)
Level BFSO(n)

Signals: levels · shortest edges · right view

q=deque([root])
while q:
    for _ in range(len(q)):
        node=q.popleft()
        if node.left:q.append(node.left)
        if node.right:q.append(node.right)
Iterative inorderO(n)

Signals: BST sorted order · avoid recursion

st=[]; cur=root
while cur or st:
    while cur: st.append(cur); cur=cur.left
    cur=st.pop(); visit(cur); cur=cur.right

14.4Visual guide

Tree traversals choose an order, not new dataread left → right
3
920
157

DFS follows a branch; BFS processes a level. Most recursive tree solutions ask: what information should each subtree return to its parent?

14.5Worked example: from brute force to the pattern

Brute-force instinct

Checking path length through every pair would be expensive. One postorder DFS returns each subtree height; at each node left_height+right_height is the best diameter passing through that node, so update a global best.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "Each child gives me height. I use those two heights both to update the best path through this node and to return one-sided height to the parent."

14.6Complexity cheat table

Operation / patternTimeSpace / note
DFS/BFS traversalO(n)O(h) DFS / O(width) BFS
max depthO(n)O(h)
diameterO(n)O(h)
build preorder+inorderO(n)O(n) index map
serialize/deserializeO(n)O(n) output/queue

14.7Mistakes that cost points

Watch out
  • Confusing node count with edge count for depth/diameter.
  • For path sum, allowing both children in the value returned to the parent when a path must be one continuous branch.
  • Not handling root=None.
  • Using preorder+inorder reconstruction with duplicate values without additional identity information.

14.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

14.9Practice set

0/15 solved10 core · 5 extra
#ProblemLevelKey idea
104Maximum Depth of Binary Tree coreEasy1 + max(left depth, right depth)
94Binary Tree Inorder Traversal coreEasyRecursive or explicit stack
102Binary Tree Level Order Traversal coreMediumQueue processed level by level
226Invert Binary Tree coreEasySwap children recursively/iteratively
543Diameter of Binary Tree coreEasyPostorder heights + global best
236Lowest Common Ancestor of a Binary Tree coreMediumTargets split across subtrees
112Path Sum coreEasyCarry remaining target down
101Symmetric Tree coreEasyCompare mirrored node pairs
100Same Tree coreEasyStructure and values must match
199Binary Tree Right Side View coreMediumLast node of each BFS level
103Binary Tree Zigzag Level Order TraversalMediumAlternate level direction
105Construct Binary Tree from Preorder and Inorder TraversalMediumRoot from preorder + inorder boundaries
572Subtree of Another TreeEasyMatch roots then same-tree check
124Binary Tree Maximum Path SumHardReturn one-sided gain; update two-sided best
297Serialize and Deserialize Binary TreeHardEncode values plus null markers
Topic 15 · ~3h · Day 25–26

Binary Search Trees

BSTs add an ordering invariant to binary trees, enabling logarithmic search when balanced and elegant inorder/range reasoning.

15.1The idea

In plain English

A BST is a filing cabinet where every left drawer contains smaller keys and every right drawer contains larger keys. The comparison tells you which half of the tree can be ignored.

  • Search and insert follow one branch according to comparisons. Balanced height gives O(log n); a sorted insertion order can degenerate to O(n) height.
  • Delete has three cases: leaf, one child, two children. For two children, replace with inorder successor/predecessor then delete that node.
  • Inorder traversal yields sorted values. kth-smallest can stop after k inorder visits.
  • Validate BST by carrying allowed lower/upper bounds; comparing only a node with its immediate children is insufficient.
  • LCA in a BST uses ordering: if both targets are smaller go left, both larger go right, otherwise current node is split/LCA.
  • Sorted array to balanced BST chooses middle as root recursively. In Python without a balanced-tree stdlib, bisect on lists or third-party SortedList are alternatives depending on constraints.

15.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

def valid(node, lo=float('-inf'), hi=float('inf')):
    if not node: return True
    if not (lo < node.val < hi): return False
    return valid(node.left, lo, node.val) and valid(node.right, node.val, hi)

15.3Pattern templates

Search/insertO(h)

Signals: ordered comparisons

cur=root
while cur:
    if x==cur.val: return cur
    cur = cur.left if x<cur.val else cur.right
Validate with boundsO(n)

Signals: global BST property

def dfs(node,lo,hi):
    if not node:return True
    if not lo < node.val < hi:return False
    return dfs(node.left,lo,node.val) and dfs(node.right,node.val,hi)
Kth inorderO(h+k) typical

Signals: kth smallest

st=[];cur=root
while True:
    while cur:st.append(cur);cur=cur.left
    cur=st.pop(); k-=1
    if k==0:return cur.val
    cur=cur.right

15.4Visual guide

BST ordering narrows the searchread left → right
8
310
1614

All keys in the left subtree are smaller; all keys in the right are larger (under the chosen duplicate policy). Inorder traversal is sorted.

15.5Worked example: from brute force to the pattern

Brute-force instinct

A common wrong solution only checks node.left.val < node.val < node.right.val. That misses a deep descendant violating an ancestor boundary. Carry the full legal interval down the recursion.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "Each recursive call receives the range every value in this subtree must satisfy, so ancestor constraints are preserved."

15.6Complexity cheat table

Operation / patternTimeSpace / note
search/insert/deleteO(h)O(h) recursion stack possible
balanced hO(log n)—
degenerate hO(n)—
inorder traversalO(n)O(h)
kth smallestO(h+k) early stopO(h)

15.7Mistakes that cost points

Watch out
  • Validating only immediate children.
  • For deletion with two children, copying successor value but forgetting to remove successor node.
  • Claiming O(log n) without stating the tree must be balanced.

15.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

15.9Practice set

0/7 solved4 core · 3 extra
#ProblemLevelKey idea
700Search in a Binary Search Tree coreEasyFollow comparisons down one branch
701Insert into a Binary Search Tree coreMediumRecurse/iterate to empty child
98Validate Binary Search Tree coreMediumCarry lower/upper bounds
230Kth Smallest Element in a BST coreMediumInorder traversal with early stop
450Delete Node in a BSTMediumLeaf/one-child/two-child cases
235Lowest Common Ancestor of a Binary Search TreeMediumBoth left, both right, or split
108Convert Sorted Array to Binary Search TreeEasyMiddle element creates balanced root
Topic 16 · ~4h · Day 26–27

Heaps & Priority Queues

Heaps maintain the next smallest/largest priority without fully sorting. Learn heapq, top-k, k-way merge and two-heap streaming median.

16.1The idea

In plain English

A heap is a tournament bracket where the winner is always at the top. You do not know the complete ranking, but you can quickly remove the current winner and repair the bracket.

  • A binary heap is an array-backed complete tree. Parent=(i-1)//2; children=2i+1,2i+2. Sift-up/down restores heap order.
  • heapq.heapify is O(n), faster than n separate pushes. heappush/heappop are O(log n), root access O(1).
  • Python heapq is min-heap; negate numeric priorities for max-heap behavior. Tuples compare lexicographically, so include a monotonic counter tie-breaker if payloads are not comparable.
  • Top-k can keep a heap of size k, O(n log k). K closest and kth largest are the same pattern with different priorities.
  • Merge k sorted lists pushes one head per list, then replaces it with the next node from that list.
  • Median of a stream uses max-heap lower half + min-heap upper half, rebalanced so sizes differ by at most one.

16.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

import heapq

# kth largest: min-heap of k largest seen
heap = []
for x in nums:
    heapq.heappush(heap, x)
    if len(heap) > k:
        heapq.heappop(heap)
return heap[0]

16.3Pattern templates

Top-k heapO(n log k)

Signals: kth largest · k closest

h=[]
for x in items:
    heapq.heappush(h, score(x))
    if len(h)>k: heapq.heappop(h)
K-way mergeO(N log k)

Signals: merge sorted sources

h=[]
for source in sources:
    push_first(source)
while h:
    value, sid, pos = heapq.heappop(h)
    output(value); push_next(sid,pos)
Two heapsO(log n) update

Signals: stream median

# low = negatives (max-heap), high = min-heap
# rebalance so len(low) >= len(high) and diff <= 1

16.4Visual guide

Heap gives fast access to the extremeread left → right

array

10312273644556

tree view

1
32
7645

heapq is a min-heap. Push/pop are O(log n); heapify is O(n). Negate priorities for max-heap behavior and add a tie-breaker when tuple payloads are not comparable.

16.5Worked example: from brute force to the pattern

Brute-force instinct

Sorting costs O(n log n). A min-heap of size k stores only the k largest values seen; its root is the kth largest. Each update costs log k, so O(n log k).

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "I only need the top k, not a total ordering, so I discard smaller elements as soon as the heap exceeds size k."

16.6Complexity cheat table

Operation / patternTimeSpace / note
peek minO(1)O(n)
push/popO(log n)O(n)
heapifyO(n)O(1) in-place list
top-kO(n log k)O(k)
merge k listsO(N log k)O(k)
median updateO(log n)O(n)

16.7Mistakes that cost points

Watch out
  • Using negation for a tuple but negating the wrong field/order.
  • Pushing (priority,obj) when obj instances cannot be compared on ties; add a counter.
  • Saying heapify is O(n log n); bottom-up heapify is O(n).

16.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

16.9Practice set

0/7 solved5 core · 2 extra
#ProblemLevelKey idea
215Kth Largest Element in an Array coreMediumSize-k min-heap or quickselect
703Kth Largest Element in a Stream coreEasyMaintain size-k min-heap
973K Closest Points to Origin coreMediumHeap/quickselect by squared distance
1046Last Stone Weight coreEasyMax-heap via negative weights
295Find Median from Data Stream coreHardMax-heap lower half + min-heap upper half
23Merge k Sorted ListsHardHeap one current node per list
621Task SchedulerMediumFrequencies + heap/greedy scheduling
Topic 17 · ~10h · Day 28–31

Graphs: BFS, DFS, Topological Sort & Union-Find

Graphs unify networks, grids, dependencies and connectivity. Master representations, traversal, components, topological order, union-find and bipartite reasoning.

17.1The idea

In plain English

A graph is a city map: intersections are vertices and roads are edges. BFS explores by distance rings; DFS follows one road as far as possible before backtracking.

  • Build adjacency lists from edge lists. For undirected edges add both directions. Complexity is O(V+E) for adjacency-list traversal.
  • BFS uses deque and marks visited on enqueue. DFS uses recursion or explicit stack. Repeating a traversal from each unseen node counts connected components.
  • Grid problems are implicit graphs: each cell has up to four/eight neighbours. Number of Islands marks a whole component; Flood Fill recolors one.
  • Multi-source BFS enqueues all sources at distance 0, useful for nearest-source distances such as rotting oranges.
  • Directed cycle detection can use DFS coloring (unvisited/visiting/done) or Kahn topological sort: if processed count < V, a cycle exists.
  • Union-find stores parent and rank/size. Path compression + union by rank gives nearly constant amortized operations and is excellent for connectivity under edge additions.
  • Bipartite check two-colors each component; an edge between equal colors proves failure. Clone Graph needs a map old node → cloned node.

17.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

from collections import deque, defaultdict

def bfs(graph, start):
    q = deque([start]); seen = {start}
    while q:
        u = q.popleft()
        for v in graph[u]:
            if v not in seen:
                seen.add(v)
                q.append(v)

17.3Pattern templates

BFS / componentsO(V+E)

Signals: shortest unweighted · layers · components

for start in graph:
    if start in seen: continue
    q=deque([start]); seen.add(start)
    while q:
        u=q.popleft()
        for v in graph[u]:
            if v not in seen: seen.add(v); q.append(v)
Kahn topological sortO(V+E)

Signals: prerequisites · DAG order

q=deque(v for v in nodes if indeg[v]==0)
order=[]
while q:
    u=q.popleft(); order.append(u)
    for v in g[u]:
        indeg[v]-=1
        if indeg[v]==0:q.append(v)
Union-Findalmost O(1)

Signals: dynamic connectivity · redundant edge

def find(x):
    if parent[x]!=x: parent[x]=find(parent[x])
    return parent[x]
def union(a,b):
    ra,rb=find(a),find(b)
    if ra==rb:return False
    # attach smaller rank/size under larger

17.4Visual guide

BFS explores rings; DFS explores branchesread left → right
ABCDE

Mark BFS nodes when you enqueue them, not when you dequeue, or duplicates can flood the queue. Topological sort applies only to DAG ordering; union-find answers connectivity incrementally.

17.5Worked example: from brute force to the pattern

Brute-force instinct

Trying every ordering is factorial. Model each prerequisite as a directed edge. Kahn’s algorithm repeatedly removes zero-indegree courses; if every course is removed, an ordering exists, otherwise a directed cycle blocks completion.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "This is cycle detection in a directed dependency graph. I use indegrees; processing all V nodes proves the graph is acyclic."

17.6Complexity cheat table

Operation / patternTimeSpace / note
BFS/DFSO(V+E)O(V)
grid traversalO(rows·cols)O(rows·cols) worst queue/seen
topological sortO(V+E)O(V)
union-find operationsamortized α(n)O(V)
clone graphO(V+E)O(V) map

17.7Mistakes that cost points

Watch out
  • For BFS, marking visited after dequeue can enqueue the same node many times.
  • For undirected adjacency, forgetting the reverse edge.
  • In DFS cycle detection, confusing a globally visited node with a node currently on the recursion stack.
  • Mutating the input grid without considering whether the caller expects it unchanged.

17.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

17.9Practice set

0/16 solved13 core · 3 extra
#ProblemLevelKey idea
200Number of Islands coreMediumDFS/BFS each unseen land component
733Flood Fill coreEasyTraverse same-color component
695Max Area of Island coreMediumComponent DFS returns/counts area
994Rotting Oranges coreMediumMulti-source BFS by minutes/layers
207Course Schedule coreMediumDirected cycle via Kahn or DFS colors
210Course Schedule II coreMediumReturn topological ordering
547Number of Provinces coreMediumComponents in adjacency matrix
684Redundant Connection coreMediumUnion-Find finds first edge closing cycle
785Is Graph Bipartite? coreMediumTwo-color every component
133Clone Graph coreMediumOld→clone map + traversal
841Keys and Rooms coreMediumReachability DFS/BFS
130Surrounded Regions coreMediumMark border-connected O cells first
417Pacific Atlantic Water Flow coreMediumReverse flow from both oceans
1091Shortest Path in Binary MatrixMediumBFS in 8 directions
802Find Eventual Safe StatesMediumDFS state or reverse topological process
127Word LadderHardBFS word transformations
Topic 18 · ~3h · Day 31–32

Shortest Paths & Minimum Spanning Trees

Choose path algorithms by edge assumptions: Dijkstra, Bellman–Ford, Floyd–Warshall, 0-1 BFS, Kruskal and Prim.

18.1The idea

In plain English

Different road maps need different planners. If every road is nonnegative, Dijkstra works; negative tolls require a method that allows later corrections.

  • Dijkstra repeatedly extracts the node with smallest known distance from a heap. Lazy deletion skips heap entries whose distance is stale.
  • Bellman–Ford relaxes all edges repeatedly and handles negative weights; bounded-edge variants model "at most K stops" by controlling relaxation rounds.
  • Floyd–Warshall uses dp[k][i][j] to allow intermediate nodes from a growing prefix, giving O(V³) all-pairs shortest paths.
  • 0-1 BFS uses a deque when weights are only 0 or 1: zero-weight moves go left, one-weight moves go right.
  • Kruskal sorts edges and uses union-find to add the cheapest edge that does not create a cycle. Prim grows one tree using a frontier heap.
  • Path reconstruction stores predecessor when a relaxation improves distance, then walks parents backward from target.

18.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

import heapq

def dijkstra(g, src):
    dist = {src: 0}
    pq = [(0, src)]
    while pq:
        d, u = heapq.heappop(pq)
        if d != dist[u]: continue
        for v, w in g[u]:
            nd = d + w
            if nd < dist.get(v, float('inf')):
                dist[v] = nd
                heapq.heappush(pq, (nd, v))
    return dist

18.3Pattern templates

DijkstraO((V+E) log V)

Signals: non-negative weights · shortest path

pq=[(0,src)]; dist={src:0}
while pq:
    d,u=heappop(pq)
    if d!=dist[u]:continue
    for v,w in g[u]:
        if d+w < dist.get(v,INF):
            dist[v]=d+w; heappush(pq,(d+w,v))
Bellman–FordO(VE)

Signals: negative edges · bounded stops

dist=[INF]*n;dist[src]=0
for _ in range(n-1):
    nxt=dist.copy()
    for u,v,w in edges:
        nxt[v]=min(nxt[v], dist[u]+w)
    dist=nxt
Kruskal MSTO(E log E)

Signals: minimum total connection · undirected

for w,u,v in sorted(edges):
    if union(u,v):
        cost += w
        used += 1

18.4Visual guide

Shortest-path algorithms differ by edge assumptionsread left → right
SABT2531

Unweighted → BFS. Non-negative weights → Dijkstra. Negative edges → Bellman–Ford. All-pairs small graph → Floyd–Warshall. MST minimizes total connection cost, not source-to-target distance.

18.5Worked example: from brute force to the pattern

Brute-force instinct

Running a fresh search to every node repeats work. Dijkstra computes shortest distances from the source in one pass over the graph frontier; the answer is the maximum finalized distance if all nodes are reachable.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "All weights are nonnegative, so once the smallest heap distance is finalized, no later path can improve it."

18.6Complexity cheat table

Operation / patternTimeSpace / note
DijkstraO((V+E) log V)O(V+E)
Bellman–FordO(VE)O(V)
Floyd–WarshallO(V³)O(V²)
0-1 BFSO(V+E)O(V)
KruskalO(E log E)O(V)
PrimO(E log V)O(V+E)

18.7Mistakes that cost points

Watch out
  • Using Dijkstra with negative edges.
  • In bounded-stop Bellman–Ford, updating dist in place can accidentally use too many edges in one round; use a copy.
  • Confusing MST with shortest-path tree; they optimize different objectives.

18.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

18.9Practice set

0/6 solved4 core · 2 extra
#ProblemLevelKey idea
743Network Delay Time coreMediumDijkstra from source; answer max distance
787Cheapest Flights Within K Stops coreMediumK+1 edge-limited relaxations or stateful shortest path
1584Min Cost to Connect All Points coreMediumMST with Prim/Kruskal
1631Path With Minimum Effort coreMediumDijkstra where path cost is max edge
1514Path with Maximum ProbabilityMediumMax-priority Dijkstra on probability product
1334Find the City With the Smallest Number of Neighbors at a Threshold DistanceMediumAll-pairs shortest paths or repeated Dijkstra
Topic 19 · ~4h · Day 32–33

Greedy & Intervals

Greedy algorithms commit to a locally best choice only when an exchange/proof argument makes that choice safe. Interval problems are the most reusable form.

19.1The idea

In plain English

A greedy cashier chooses the best-looking move now and never revisits it. That is only correct when you can prove an optimal solution can be rearranged to start with the same choice.

  • Greedy needs a correctness argument, often exchange: replace the first choice of an optimal solution with the greedy choice without worsening the rest.
  • Coin choice is not universally greedy: coins [1,3,4], amount 6 — taking 4 leads to 4+1+1 (3 coins), but 3+3 uses 2.
  • Interval scheduling maximizes non-overlapping intervals by sorting by end time and keeping the earliest finishing compatible interval.
  • Jump Game tracks farthest reachable index; Jump Game II expands reach by levels/segments. Gas Station uses total feasibility plus reset of negative prefix.
  • Partition Labels tracks the last occurrence of each character and closes a partition when current index reaches the farthest last occurrence seen.
  • Merge/insert intervals sort or exploit order; Non-overlapping Intervals and arrows are end-time greedy. Meeting-room counting is a sweep-line/heap technique even when premium titles are avoided.

19.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

# maximum number of non-overlapping intervals
intervals.sort(key=lambda x: x[1])
end = float('-inf')
kept = 0
for s, e in intervals:
    if s >= end:
        kept += 1
        end = e

19.3Pattern templates

Earliest finishO(n log n)

Signals: interval scheduling · removals

intervals.sort(key=lambda x:x[1])
end=-INF
for s,e in intervals:
    if s>=end:
        keep(); end=e
Farthest reachO(n)

Signals: jump game · coverage

far=0
for i,jump in enumerate(nums):
    if i>far:return False
    far=max(far,i+jump)
return True
Gas-station resetO(n)

Signals: circular route · net gain

if sum(gas)<sum(cost):return -1
start=tank=0
for i in range(n):
    tank += gas[i]-cost[i]
    if tank<0:start=i+1;tank=0
return start

19.4Visual guide

Greedy interval scheduling: keep the earliest finishread left → right

A greedy rule needs a proof idea: exchange an optimal choice with the greedy choice without making the solution worse. When that argument fails, dynamic programming may be required.

19.5Worked example: from brute force to the pattern

Brute-force instinct

Checking all subsets is exponential. Sort by end time; keeping the interval that finishes earliest leaves at least as much room for every future interval as any later-finishing choice.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "The exchange argument is that replacing a kept interval with an earlier-finishing compatible one cannot reduce future options."

19.6Complexity cheat table

Operation / patternTimeSpace / note
interval sortO(n log n)O(1) extra aside from sort
jump gameO(n)O(1)
gas stationO(n)O(1)
partition labelsO(n)O(alphabet)
merge intervalsO(n log n)O(n) output

19.7Mistakes that cost points

Watch out
  • Applying greedy without a proof or monotonic invariant.
  • Sorting intervals by start when the goal requires earliest finish.
  • In Jump Game II, incrementing jumps per index instead of per completed reachable segment.
  • For interval overlap, not clarifying whether touching endpoints overlap.

19.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

19.9Practice set

0/10 solved5 core · 5 extra
#ProblemLevelKey idea
455Assign Cookies coreEasySort both; give smallest sufficient cookie
55Jump Game coreMediumTrack farthest reachable index
45Jump Game II coreMediumGreedy BFS-style range expansion
134Gas Station coreMediumTotal feasibility + reset failed starts
763Partition Labels coreMediumClose when reaching farthest last occurrence
860Lemonade ChangeEasyPreserve flexible $5 bills; greedy change
56Merge IntervalsMediumSort by start and merge overlap
57Insert IntervalMediumAdd before/merge/after in ordered list
435Non-overlapping IntervalsMediumKeep earliest finishing interval
452Minimum Number of Arrows to Burst BalloonsMediumShoot at earliest end among overlapping balloons
Topic 20 · ~13h · Day 33–37

Dynamic Programming

Dynamic programming is the biggest pattern family: define states and transitions, then cache or tabulate them. Learn 1D, knapsack, strings, grids and interval-style thinking.

20.1The idea

In plain English

DP is filling a workbook where each answer may reuse answers from earlier pages. The hard part is deciding what one cell means; after that, the recurrence and order follow.

  • The five-part framework: state (what does dp[...] mean?), transition, base cases, computation order, answer location.
  • Memoization is top-down recursion + cache; tabulation is bottom-up iteration. Convert by identifying dependencies and ordering states so prerequisites are ready first.
  • Climbing Stairs and House Robber show 1D recurrence and O(1) rolling-state optimization. House Robber II splits the circle into two linear cases.
  • Decode Ways checks one-digit and valid two-digit transitions. Coin Change minimum uses min; Coin Change II counts ways and loop order controls whether combinations or permutations are counted.
  • 0/1 knapsack/Partition Equal Subset Sum update capacities backward so each item is used once. Unbounded coin change can iterate capacity forward.
  • LIS has O(n²) DP and O(n log n) patience/lower-bound optimization. Grid DP comes from top/left neighbours.
  • LCS and Edit Distance are 2D prefix-state string DPs. Palindrome DP expands centers or stores interval truth. Word Break uses prefix reachability.
  • Stock with cooldown models holding/not-holding/cooldown states. Interval DP chooses a last/first operation inside a range (e.g. Burst Balloons) and is an advanced extension.

20.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

# 0/1 subset-sum feasibility
can = [False] * (target + 1)
can[0] = True
for x in nums:
    for s in range(target, x - 1, -1):
        can[s] |= can[s - x]
return can[target]

20.3Pattern templates

Memo DPO(states × transitions)

Signals: recursive recurrence · sparse states

from functools import cache
@cache
def dp(state):
    if base(state): return base_value
    return best(dp(next_state) + cost for next_state in moves(state))
1D rolling DPO(n), O(1) space

Signals: stairs · robber

prev2,prev1=base0,base1
for i in range(2,n+1):
    cur=transition(prev2,prev1)
    prev2,prev1=prev1,cur
0/1 knapsackO(n·target)

Signals: choose each item once

dp=[0]*(cap+1)
for w,val in items:
    for c in range(cap,w-1,-1):
        dp[c]=max(dp[c],dp[c-w]+val)
2D string DPO(mn)

Signals: LCS · edit distance

dp=[[0]*(n+1) for _ in range(m+1)]
for i in range(1,m+1):
    for j in range(1,n+1):
        dp[i][j]=transition(...)

20.4Visual guide

DP table = saved answers to overlapping statesread left → right
1111112345136101514102035

For every DP problem name the state, transition, base cases, computation order and final answer. Then ask whether old rows/columns can be discarded to optimize space.

20.5Worked example: from brute force to the pattern

Brute-force instinct

Naive recursion tries many repeated remaining amounts. Define dp[a] as the fewest coins to form amount a. Each state considers dp[a-coin]+1, so tabulation solves each amount once. Impossible states stay infinity.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "My state is the best answer for one remaining/constructed amount. Because transitions only depend on smaller amounts, I can fill amounts from 0 upward."

20.6Complexity cheat table

Operation / patternTimeSpace / note
1D DPoften O(n)O(n) → O(1) if local deps
knapsack/subsetO(n·target)O(target)
LIS classicO(n²)O(n)
LIS optimizedO(n log n)O(n)
grid DPO(mn)O(mn) or O(n)
LCS/edit distanceO(mn)O(mn) or row-optimized
interval DPoften O(n³)O(n²)

20.7Mistakes that cost points

Watch out
  • Writing code before defining the state in one sentence.
  • For 0/1 knapsack, iterating capacity forward and accidentally reusing the same item.
  • For Coin Change II, changing loop order and counting permutations instead of combinations.
  • Using 0 as an impossible min-cost state; use infinity/sentinel.
  • Optimizing space before verifying the full-table recurrence.

20.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

20.9Practice set

0/21 solved17 core · 4 extra
#ProblemLevelKey idea
70Climbing Stairs coreEasydp[i]=dp[i-1]+dp[i-2]
198House Robber coreMediummax(skip, take + dp[i-2])
213House Robber II coreMediumMax of two linear ranges excluding one endpoint
91Decode Ways coreMediumOne-digit and valid two-digit transitions
322Coin Change coreMediumMinimum over dp[amount-coin]+1
518Coin Change II coreMediumCount combinations; coin outer loop
416Partition Equal Subset Sum coreMedium0/1 subset sum to total/2
300Longest Increasing Subsequence coreMediumO(n²) DP or tails + bisect_left
62Unique Paths coreMediumTop + left grid recurrence
64Minimum Path Sum coreMediumCell cost + min(top,left)
1143Longest Common Subsequence coreMediumPrefix pair DP
72Edit Distance coreMediumInsert/delete/replace recurrence
139Word Break coreMediumReachable prefix DP
647Palindromic Substrings coreMediumExpand centers or interval DP
309Best Time to Buy and Sell Stock with Cooldown coreMediumState machine DP
494Target Sum coreMediumSigned-sum memo or subset transform
221Maximal Square coreMedium1 + min(top,left,diag) for 1-cells
120TriangleMediumBottom-up min path
5Longest Palindromic SubstringMediumExpand around every center
312Burst BalloonsHardInterval DP choose last balloon
115Distinct SubsequencesHardCount prefix matches
Topic 21 · ~2h · Day 38

Tries (Prefix Trees)

Tries index strings by prefix, making search and autocomplete depend on word length rather than the total number of stored words.

21.1The idea

In plain English

A trie is a dictionary where words sharing the same beginning literally share the same hallway until their letters diverge.

  • A TrieNode stores children mapping character→node and is_end to distinguish complete words.
  • Insert walks/creates one node per character. Search requires every character and a terminal flag. startsWith requires only the path.
  • A compact Python version can use nested dicts with a special terminal marker.
  • Autocomplete walks to the prefix node then DFS/BFS collects terminal descendants, often stopping after k suggestions.
  • Wildcard "." search branches across all children at that position, so worst-case can grow exponentially in the number of wildcards.
  • Word Search II combines trie prefix pruning with board DFS so searches for many words share work.

21.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_end = False

class Trie:
    def __init__(self): self.root = TrieNode()
    def insert(self, word):
        node = self.root
        for ch in word:
            node = node.children.setdefault(ch, TrieNode())
        node.is_end = True

21.3Pattern templates

Insert/searchO(L)

Signals: dictionary words · prefixes

node=root
for ch in word:
    if ch not in node.children:return False
    node=node.children[ch]
return node.is_end
AutocompleteO(prefix + output)

Signals: suggestions · prefix query

node=walk(prefix)
# DFS descendants, append when is_end, stop after k
Wildcard DFSbranching on .

Signals: pattern with dot wildcard

def dfs(i,node):
    if i==len(word): return node.is_end
    if word[i]=='.': return any(dfs(i+1,c) for c in node.children.values())
    return word[i] in node.children and dfs(i+1,node.children[word[i]])

21.4Visual guide

Trie shares prefixesread left → right
∅
cd
ao
trg

Each edge is a character; a terminal flag distinguishes a complete word from a mere prefix. Search time is O(length of word), independent of how many words share the prefix.

21.5Worked example: from brute force to the pattern

Brute-force instinct

A normal trie search follows one edge per letter. A dot wildcard cannot choose a single edge, so recurse into every child only at wildcard positions while normal characters remain O(1)-branch.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "The trie prunes by fixed prefixes; wildcard positions branch over existing children rather than the entire alphabet."

21.6Complexity cheat table

Operation / patternTimeSpace / note
insertO(L)O(L) new nodes worst
exact searchO(L)O(1) extra iterative
prefix searchO(L)O(1)
autocompleteO(L + visited/output)depends on subtree
wildcardworst exponential in wildcardsrecursion depth O(L)

21.7Mistakes that cost points

Watch out
  • Treating startsWith as search and requiring is_end.
  • Using a shared mutable default children dict in a node constructor.
  • For autocomplete, forgetting to bound output/DFS when only k suggestions are required.

21.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

21.9Practice set

0/5 solved3 core · 2 extra
#ProblemLevelKey idea
208Implement Trie (Prefix Tree) coreMediumchildren dict + terminal flag
211Design Add and Search Words Data Structure coreMediumDFS branches on dot wildcard
212Word Search II coreHardBoard DFS pruned by trie prefixes
648Replace WordsMediumFind shortest terminal prefix
1268Search Suggestions SystemMediumTrie or sorted range for each prefix
Topic 22 · ~2h · Day 38–39

Bit Manipulation & Math

Bits turn parity, masks and powers of two into constant-time operations; math utilities cover gcd, sieve, modular power and digit manipulation.

22.1The idea

In plain English

An integer is a row of on/off switches. Bit operations let you test, flip, combine or remove switches without converting to strings.

  • Operators: & AND, | OR, ^ XOR, ~ NOT, << left shift, >> right shift. Test bit i with (x>>i)&1; set with x | (1<
  • x & (x-1) clears the lowest set bit; x & -x isolates the lowest set bit (two’s-complement reasoning).
  • XOR cancels pairs: a^a=0 and a^0=a, powering Single Number and missing-value tricks.
  • Bitmask subsets iterate mask from 0 to (1<
  • Python integers are unbounded and negative shifts/sign behave differently from fixed 32-bit machine ints; use & 0xFFFFFFFF where a problem requires unsigned 32-bit representation.
  • math.gcd and math.lcm are standard. Sieve marks composite multiples. pow(a,b,mod) performs fast modular exponentiation; 10**9+7 is a common modulus when a problem specifies it.
  • Digit manipulation uses divmod(x,10), repeated %10 and //10; watch overflow rules in problems that impose 32-bit signed limits.

22.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

# Kernighan popcount
count = 0
while x:
    x &= x - 1
    count += 1

# enumerate subsets by mask
for mask in range(1 << n):
    subset = [a[i] for i in range(n) if mask & (1 << i)]

22.3Pattern templates

XOR cancellationO(n)

Signals: single odd-occurring value

ans=0
for x in nums: ans ^= x
return ans
Clear lowest set bitO(number of 1 bits)

Signals: popcount · bit iteration

count=0
while x:
    x &= x-1
    count += 1
SieveO(n log log n)

Signals: all primes up to n

prime=[True]*(n+1)
prime[:2]=[False,False]
for p in range(2,int(n**0.5)+1):
    if prime[p]:
        for m in range(p*p,n+1,p): prime[m]=False

22.4Visual guide

Bit tricks operate on representation directlyread left → right
x = 4400101100x & (x-1)00101000

x & (x-1) removes the lowest set bit. XOR cancels equal values. Remember Python integers are unbounded, so fixed-width bit problems may need a 32-bit mask.

22.5Worked example: from brute force to the pattern

Brute-force instinct

A set/count map works in O(n) time and O(n) space. XOR uses the algebraic cancellation property to remove paired values in O(n) time and O(1) extra space.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "Because XOR is associative/commutative and x^x=0, all paired numbers cancel and only the unpaired value remains."

22.6Complexity cheat table

Operation / patternTimeSpace / note
bit test/setO(1)O(1)
Kernighan countO(popcount)O(1)
bitmask subsetsO(n·2^n)O(n) current subset
gcdO(log min(a,b))O(1)
sieveO(n log log n)O(n)
pow(a,b,mod)O(log b)O(1)

22.7Mistakes that cost points

Watch out
  • Using ~x expecting a fixed-width complement in Python; ~x == -x-1.
  • For reverse-integer problems, forgetting the specified 32-bit range.
  • Using floating-point sqrt/pow when an exact integer algorithm is required.

22.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

22.9Practice set

0/9 solved3 core · 6 extra
#ProblemLevelKey idea
136Single Number coreEasyXOR all values
191Number of 1 Bits coreEasyx &= x-1 repeatedly
338Counting Bits coreEasydp[i]=dp[i>>1]+(i&1)
190Reverse BitsEasyShift result and consume 32 bits
268Missing NumberEasyXOR indices and values or arithmetic sum
371Sum of Two IntegersMediumXOR sum + AND carry under 32-bit mask
202Happy NumberEasyDigit-square transform + cycle detection
204Count PrimesMediumSieve of Eratosthenes
7Reverse IntegerMediumRebuild digits with range check
Topic 23 · ~5h · Day 39–40

Mock Interviews & Final Review

Convert knowledge into interview performance: a repeatable 45-minute flow, pattern recognition, timed-test discipline and a final error log.

23.1The idea

In plain English

A mock interview is a flight simulator: the point is not to prove you know everything, but to practise the exact sequence of clarifying, reasoning, coding, testing and explaining under time pressure.

  • 45-minute flow: clarify examples/constraints → state brute force → derive pattern/optimization → code → dry-run → edge cases → complexity and follow-ups.
  • Think aloud in useful chunks: state invariant, why a data structure fits, what each loop guarantees, and what you will test. Do not narrate syntax keystrokes.
  • Online-test strategy: scan all tasks first, secure easy/medium points, watch constraints, write a correct baseline quickly, then optimize bottlenecks. Keep 10–15% of time for tests.
  • Edge-case checklist: empty/minimum input, one element, duplicates, all equal, sorted/reverse, negative/zero, overflow rules, disconnected graph, skewed tree, impossible answer.
  • Signal→pattern: sorted pair→two pointers; contiguous longest/shortest→window; next greater→monotonic stack; prerequisite→toposort; connectivity→union-find; nonnegative weighted path→Dijkstra; repeated choices→DP/backtracking depending overlap.
  • Keep an error log with problem, missed signal, wrong assumption, bug type and corrected pattern. Re-solve after spaced intervals.
  • Record yourself solving aloud. For the final week, repeat core problems, do three 45-minute mocks, review errors, and avoid learning large new topic families.

23.2Python toolkit

Use these idioms as a starting point, then adapt them to the problem's exact constraints and return format.

def run_tests(fn, cases):
    for args, expected in cases:
        got = fn(*args)
        status = "PASS" if got == expected else "FAIL"
        print(status, args, "=>", got, "expected", expected)

# Deliberate boundary bug to catch in a mock:
def lower_bound_bug(a, x):
    lo, hi = 0, len(a) - 1   # wrong for insertion at len(a)
    while lo < hi:
        mid = (lo + hi) // 2
        if a[mid] < x: lo = mid + 1
        else: hi = mid
    return lo

23.3Pattern templates

Interview loop45 min

Signals: live coding · think aloud

clarify()
brute_force()
state_complexity()
optimize_by_pattern()
code_cleanly()
dry_run()
test_edges()
state_final_complexity()
Error log10 min after practice

Signals: repeat mistakes deliberately

log = {
 "signal_missed": "contiguous + longest",
 "bug": "used if instead of while when shrinking",
 "fix": "state window invariant first"
}
Timed-test triagewhole test

Signals: maximize correct submissions

# 1 scan all
# 2 solve easiest complete tasks
# 3 optimize by constraints
# 4 reserve test/review time

23.4Visual guide

45-minute interview flowread left → right
0–5 clarify→5–10 brute force→10–15 optimize→15–35 code→35–42 test→42–45 complexity

Narrate decisions, not every keystroke. State assumptions, test edge cases, and leave enough time to verify complexity and one manual example.

23.5Worked example: from brute force to the pattern

Brute-force instinct

Do not jump directly to memorized code. For each problem, state the brute force and why it fails the constraints, identify the structural signal, state an invariant, then implement. This makes the solution adaptable if the interviewer changes the requirement.

Pattern-level solution

The optimized approach follows the invariant taught above and removes repeated work or impossible candidates.

How to say it in an interview: "I’ll first confirm constraints, give the simplest correct approach, then improve the bottleneck. I’ll state the invariant before coding so we can verify correctness as we go."

23.6Complexity cheat table

Operation / patternTimeSpace / note
clarification~5 minrequirements/examples
brute + optimize~10 mincomplexity/pattern
implementation~20 minclean code
tests~7 minedge cases/dry run
complexity/follow-up~3 mintrade-offs

23.7Mistakes that cost points

Watch out
  • Silently coding for 20 minutes.
  • Memorizing templates without knowing the invariant.
  • Skipping edge cases after getting a sample to pass.
  • Changing algorithms mid-code without resetting assumptions and variable meanings.
  • Doing only new problems instead of revisiting mistakes.

23.8BD interview angle

Interview use

In Bangladesh-style timed assessments and live coding, recognise the pattern quickly, state the target complexity before coding, and test boundary cases. Python is typically acceptable because hiring is usually language-agnostic. Practise explaining the solution aloud and record yourself periodically.

23.9Practice set

0/9 solved7 core · 2 extra
#ProblemLevelKey idea
217Contains Duplicate coreEasyMock A easy: seen set
3Longest Substring Without Repeating Characters coreMediumMock A medium: variable window
56Merge Intervals coreMediumMock A medium: sort and merge
704Binary Search coreEasyMock B easy: exact invariant
198House Robber coreMediumMock B medium: 1D DP
207Course Schedule coreMediumMock B medium: topological cycle check
121Best Time to Buy and Sell Stock coreEasyMock C easy: running minimum
200Number of IslandsMediumMock C medium: grid components
215Kth Largest Element in an ArrayMediumMock C medium: heap or quickselect
master practice registry

LeetCode problem tracker

All topic practice in one place. Filters and solved state run locally in your browser.

fast pattern recognition

Static visual atlas

Jump to the topic whose shape matches the problem in front of you.

Contiguous range

Longest/shortest substring or subarray with a changing condition.

Sliding Window →
Sorted pair / in-place compaction

Opposite ends or read/write indices.

Two Pointers →
Repeated lookup / counting

Complement, frequency, group or seen membership.

Hashing →
Next greater / nested structure

Unresolved candidates or LIFO matching.

Stacks →
Sorted monotonic decision

Target search or minimum feasible answer.

Binary Search →
Hierarchy

Subtree returns, levels, ancestor relations.

Trees →
Connectivity / dependencies

Traversal, topological order, union-find.

Graphs →
Weighted network

Shortest path, all-pairs or minimum spanning tree.

Shortest Paths →
Overlapping subproblems

State, transition and cached/tabulated answers.

Dynamic Programming →
Enumerate choices

Decision tree with choose/explore/unchoose.

Backtracking →
Repeated min/max priority

Top-k, scheduling, merge-k, median stream.

Heaps →
Prefixes of words

Shared prefix paths, autocomplete, wildcard search.

Tries →
interview recall

Pattern cheat sheet

Use this before mocks; in a real interview, derive the pattern from constraints rather than keyword matching alone.

1If you see X → think Y

SignalLikely pattern
sorted + pair/closesttwo pointers
contiguous longest/shortestsliding window
many range sumsprefix sums
count/lookup/complementhashing
next greater / nearest smallermonotonic stack
minimum feasible / maximum possiblebinary search on answer
unweighted shortest pathBFS
dependency orderingtopological sort
dynamic connectivityunion-find
nonnegative weighted shortest pathDijkstra
all combinations/arrangementsbacktracking
overlapping states / optimize choicesDP

2Constraints → complexity target

Typical nUsually reasonable
≤ 202^n / backtracking may fit
≤ 100n³ sometimes
≤ 1,000n² often
≤ 100,000n log n or n
≥ 1,000,000near-linear / streaming

3Python operation costs

OperationCost
list indexO(1)
list appendamortized O(1)
list insert/pop at frontO(n)
dict/set membershipaverage O(1)
deque append/popleftO(1)
heap push/popO(log n)
sortO(n log n)
slice of k itemsO(k)

4Compact templates

Two pointers

l,r=0,len(a)-1
while l<r:
    if ok(a[l],a[r]): ...
    elif need_bigger: l+=1
    else: r-=1

Sliding window

l=0
for r,x in enumerate(a):
    add(x)
    while invalid(): remove(a[l]); l+=1
    best=max(best,r-l+1)

Lower bound

lo,hi=0,len(a)
while lo<hi:
    mid=(lo+hi)//2
    if a[mid] < x: lo=mid+1
    else: hi=mid

BFS

q=deque([s]); seen={s}
while q:
    u=q.popleft()
    for v in g[u]:
        if v not in seen: seen.add(v); q.append(v)

Grid DFS

def dfs(r,c):
    if out_or_blocked(r,c): return
    mark(r,c)
    for dr,dc in DIRS: dfs(r+dr,c+dc)

Topological sort

q=deque(v for v in nodes if indeg[v]==0)
while q:
    u=q.popleft(); order.append(u)
    for v in g[u]:
        indeg[v]-=1
        if indeg[v]==0:q.append(v)

Union-Find

def find(x):
    if parent[x]!=x: parent[x]=find(parent[x])
    return parent[x]

Dijkstra

pq=[(0,s)];dist={s:0}
while pq:
    d,u=heappop(pq)
    if d!=dist[u]:continue
    for v,w in g[u]:
        if d+w < dist.get(v,INF): ...

Backtracking

def dfs(start):
    if done(): out.append(path[:]); return
    for i in range(start,n):
        choose(i); dfs(i+1); unchoose(i)

Memo DP

@cache
def dp(state):
    if base(state): return base_value
    return best(dp(nxt)+cost for nxt in moves(state))

Monotonic stack

st=[]
for i,x in enumerate(a):
    while st and a[st[-1]] < x:
        j=st.pop(); ans[j]=i
    st.append(i)

Heap top-k

h=[]
for x in items:
    heappush(h,x)
    if len(h)>k:heappop(h)

Trie

node=root
for ch in word:
    node=node.children.setdefault(ch,Node())
node.is_end=True

Prefix + hashmap

freq={0:1};p=ans=0
for x in a:
    p+=x; ans+=freq.get(p-k,0)
    freq[p]=freq.get(p,0)+1

5Edge-case checklist

  • empty/minimum input
  • one element
  • duplicates/all equal
  • already sorted/reverse sorted
  • zero/negative values
  • impossible answer
  • off-by-one boundaries
  • disconnected graph / null tree
  • mutation and aliasing