Most DSA tutorials show you the answer. They present a clean, optimal solution, walk through the code line by line, and call it done. That's useful — but it skips the most important part: how do you arrive at that solution when you're staring at a blank screen in an interview?
This post is about the thinking process, not just the output. I'll walk through three problems — one from each of the most common interview categories — and show exactly how I reason from first principles to an optimal solution.
Problem 1: Two Sum (Arrays + Hashing)
Given an array of integers and a target, return the indices of two numbers that add up to the target.
Step 1 — Brute force first
Always start here. Two nested loops, check every pair:
def two_sum_brute(nums, target):
for i in range(len(nums)):
for j in range(i + 1, len(nums)):
if nums[i] + nums[j] == target:
return [i, j]
Time complexity: O(n²). Works, but won't pass large inputs.
Step 2 — Identify the bottleneck
The inner loop is doing repeated lookups. What data structure gives us O(1) lookup? A hash map.
Step 3 — Reframe the problem
Instead of "find two numbers that sum to target," think: "for each number, check if its complement (target - num) already exists in what I've seen so far."
def two_sum(nums, target):
seen = {}
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [seen[complement], i]
seen[num] = i
Time complexity: O(n). One pass, one hash map. The insight: whenever you're doing repeated searches inside a loop, ask if a hash map can eliminate the inner scan.
Problem 2: Longest Substring Without Repeating Characters (Sliding Window)
Given a string, find the length of the longest substring without repeating characters.
Step 1 — Recognize the pattern
We're dealing with a contiguous sequence and a constraint (no repeats). This is the sliding window pattern.
Step 2 — Build the window
def length_of_longest_substring(s):
char_index = {}
left = 0
max_len = 0
for right, char in enumerate(s):
if char in char_index and char_index[char] >= left:
left = char_index[char] + 1
char_index[char] = right
max_len = max(max_len, right - left + 1)
return max_len
Time complexity: O(n). The key line is char_index[char] >= left — it ensures we only shrink the window based on characters inside the current window, not stale entries.
The insight: sliding window works on any problem asking for an optimal contiguous subarray or substring under a constraint.
Problem 3: Climbing Stairs (Dynamic Programming)
You can climb 1 or 2 steps at a time. How many distinct ways can you reach the nth step?
Step 1 — Think recursively first
To reach step n, you either came from step n-1 or step n-2. So ways(n) = ways(n-1) + ways(n-2) — that's just Fibonacci.
Step 2 — Optimize space
You only ever need the last two values:
def climb_stairs(n):
if n <= 2:
return n
a, b = 1, 2
for _ in range(3, n + 1):
a, b = b, a + b
return b
O(n) time, O(1) space. The insight: DP problems follow the same arc — recursive intuition → memoization → bottom-up iteration → space optimization.
The Meta-Framework I Use for Every Problem
- Understand before coding — Restate the problem. Identify input/output. Ask about edge cases.
- Brute force out loud — Never jump to optimal. Shows structured thinking and gives a correctness baseline.
- Identify the bottleneck — Repeated scanning → hash map. Redundant subproblems → DP. Sorted data → binary search. Contiguous constraint → sliding window.
- Code the optimized solution — Only after reasoning through the approach verbally.
- Test with examples — Happy path first, then edge cases.
Patterns Worth Drilling
- Two pointers — sorted arrays, palindromes, pair sums
- Sliding window — substring/subarray problems with constraints
- Hash map for O(1) lookup — any "have I seen this before" problem
- BFS/DFS on graphs and trees — traversal, shortest path, connected components
- Dynamic programming — optimization problems with overlapping subproblems
- Binary search — not just on sorted arrays, but on answer spaces
One problem per pattern per day is more effective than grinding 10 random problems.
A Note on Language Choice
I solve in Python for interviews. The syntax is concise, collections are built-in, and you spend less time on boilerplate. collections.defaultdict, heapq, and collections.deque cover 80% of what you need beyond basic arrays and hash maps.