2Brute2Best
🔥0
DSA Problem-Solving Playbook

How to approach any new DSA problem from scratch.

Don't try to remember 400 solutions. Follow the same six steps every time — from Brute → Better → Optimal — and use the decision tree to pick the pattern.

01

The 6-step thinking process (use this on every problem)

01

Recognise — read the input and the constraints

Minutes 0–3

Before thinking about code, decide what kind of problem this is. The input shape and the limits already tell you which patterns are possible.

  • •

    Input shape: array / string (sorted or not?), linked list, tree, graph or grid, intervals, a stream.

  • •

    What is asked: count, min / max, exists?, return ALL answers, Kth item, or an order.

  • •

    Constraints → target complexity: n ≤ 20 → 2^n is fine; n ≤ 10^3 → O(n²); n ≤ 10^5 → O(n log n) or O(n).

  • •

    Keywords: "contiguous", "at most K", "sorted", "next greater", "all combinations", "minimum cost" — each is a pattern clue (see the tree below).

02

Brute force — say the obvious slow solution out loud

Minutes 3–8

Always start with the simplest correct idea, even if it is too slow. It proves you understood the problem and becomes your reference answer for testing.

  • •

    Try every subarray / pair / subset / path that the question allows.

  • •

    State its time and space complexity, and compare with the target from step 1.

  • •

    Keep this code: later you will compare the optimal against it on random inputs.

03

Repeated work — find what brute force computes again and again

Minutes 8–15

The optimal solution almost always comes from removing repeated work. Ask: which values does brute force recompute, and can I reuse them?

  • •

    Neighbouring windows overlap → Sliding Window. Range sums recomputed → Prefix Sum.

  • •

    Searching again for a complement / seen value → Hash Map. Searching in sorted data → Binary Search.

  • •

    Same sub-problem solved many times → DP (memo / table). Re-scanning for the next bigger value → Monotonic Stack.

  • •

    Repeatedly picking the smallest / largest → Heap.

04

State + validity — decide what to keep and when it is valid

Minutes 15–20

Write down the smallest amount of information you carry while scanning, and the rule (invariant) that is true at every step.

  • •

    Examples of state: window counts, dp[i], a heap of size K, a stack of candidates, parent[] in Union-Find.

  • •

    Validity rule: "the window has at most K distinct", "the stack is decreasing", "dp[i] is the best answer ending at i".

  • •

    If you cannot say the invariant in one sentence, the state is probably wrong.

05

Transitions — when to move what, and why it is safe

Minutes 20–25

Decide exactly how the state changes on each step, and argue why you never skip the answer.

  • •

    Pointers: when do you grow, when do you shrink, and why can a skipped position never be the answer?

  • •

    DP: which smaller states does dp[i] depend on, and in what loop order?

  • •

    Greedy: name the proof — exchange argument, stays ahead, or elimination.

06

Code + test — write it, dry-run it, compare

Minutes 25–45

Write clean code, then trace it on the example by hand before running it.

  • •

    Dry-run the given example and check the state after every step.

  • •

    Edge cases: empty input, one element, all equal, negatives, duplicates, int overflow.

  • •

    Compare the optimal with your brute force on small random inputs.

02

Clue → pattern decision tree

Start on the left with what the input looks like, follow the clue that matches your problem, and land on a pattern. Click a pattern to open its lesson.

Read the problem: what is the input, what is asked?
Input is an array or a string
It is sorted (or sorting would help)
Find a target, first/last position, or a boundaryBinary Search →
"Minimum possible maximum" / smallest value that worksBinary Search →
Pair / triplet with a given sum, remove duplicates in placeTwo Pointers →
Pick greedily after sorting (meetings, jumps, assignments)Greedy →
Asks about a contiguous subarray / substring
Longest / shortest window with a condition (at most K, all distinct)Sliding Window →
Count subarrays with sum = K, or negatives are allowedPrefix Sum →
Maximum sum / product subarrayDP — Kadane / Subarray →
Next greater / smaller element, span, histogram areaMonotonic Stack →
Values are in range 1..n — find missing / duplicateCyclic Sort →
Frequencies, duplicates, anagrams, "seen before?"Arrays & Hashing →
Matching brackets, nesting, evaluate an expressionStack →
Intervals [start, end] — merge, overlap, insertIntervals →
Top K / Kth largest / K closestHeap — Top K →
Running median / balance two halvesTwo Heaps →
Merge K sorted lists / arrays, Kth smallest across rowsK-way Merge →
XOR tricks, "every element twice except one", bit masksBit Manipulation →
Prefix search, word dictionary, autocompleteTrie →
Input is a linked list
Cycle, middle, Nth from endLinked List — Fast & Slow →
Reverse, reorder, reverse in groups of KLinked List — Reversal →
Input is a tree
Level by level, minimum depth, side viewsTree BFS →
Paths, heights, LCA, validate / build a BSTTree DFS →
Input is a graph or a 2-D grid
Islands, connected parts, reachability, unweighted shortest pathGraph BFS / DFS →
Dependencies / prerequisites / order of tasksTopological Sort →
Merge groups, redundant edge, "are they connected?" many timesUnion-Find (DSU) →
Weighted shortest path / cheapest costShortest Paths (Weighted) →
Asks for ALL combinations / permutations / subsets / placementsBacktracking →
Count ways / min cost / max value, and choices repeat (overlapping subproblems)
One sequence, a decision at each index (stairs, house robber)DP — 1D Linear →
Paths in a gridDP — Grid →
Pick items under a capacity / target sumDP — Knapsack →
Increasing subsequence, chains, envelopesDP — Longest Increasing Subsequence →
Two strings (LCS, edit distance) or palindromesDP — Strings →
A local best choice is always safe (prove by exchange)Greedy →