This guide was created by PROGSU to help students master technical interviews with a practical, no-nonsense playbook. Use it to train like a team, level up fast, and show up prepared.
- 1. How to Use This Guide
- 2. What Interviewers Actually Test
- 3. Interview Basics
- 4. Problem Types You’ll See
- 5. The 25-Minute Game Plan
- 6. Technique Library
- 7. Worked Example: Two Sum
- 8. Pitfalls and Damage Control
- 9. Practice Plan
- 10. Final Checklist
This is a field manual, not a textbook. Skim the structure, then practice with a timer. Speak your thoughts out loud, write short bullet notes as comments, and hold yourself to clean code and clear communication. Repeat until your process feels automatic.
- Problem decomposition: Can you restate the question precisely and define constraints?
- Algorithmic judgment: Do you explore trade-offs and pick a sound approach?
- Data structure fluency: Do you use the right tools and explain why?
- Complexity awareness: Can you reason about time and space at each step?
- Execution and hygiene: Can you code cleanly, test smartly, and fix quickly?
- Communication: Can we follow your thinking without guessing?
Passing is rarely about brilliance. It’s about running a tight process under a clock.
- Duration: 45–50 minutes total.
- Coding portion: typically 1–2 problems, ~25 minutes each.
- Setting: collaborative editor (no IDE autocompletion), occasionally a whiteboard.
- Interviewers: usually 1–2 engineers. Expect light behavioral chat.
- Language choice: Pick one and train in it. Recommended: Python. Other common picks: Java, C++, JavaScript.
- Lookups allowed (ask first): official language docs for syntax.
- Not allowed: AI tools, searching for solutions, copying snippets from forums.
- Editor reality: Fewer helpers than your IDE. Type carefully and narrate.
- Data Structures & Algorithms (most common): arrays, strings, hash maps/sets, stacks/queues, linked lists, trees, graphs, heaps; patterns like two pointers, sliding window, BFS/DFS, binary search, greedy, DP.
- Object-Oriented Design: design a small system or class (LRU cache, parking lot, library checkout). Emphasis on modeling, invariants, and clean APIs.
- System Design (rare for early roles): high-level reasoning about scalability and trade-offs. Know the vocabulary; depth comes later.
Time is adjustable, but the rhythm should feel like this:
- 0–5: Understand. Read out loud. Restate inputs/outputs. Lock constraints. List edge cases.
- 5–12: Ideate. Outline brute force, then improve. Compare time/space. Choose and confirm.
- 12–20: Implement. Code with small, labeled steps. Keep talking. Handle edge cases.
- 20–25: Review. Walk through tests. State complexity. Offer incremental optimizations.
If you’re blocked at minute ~12, make a decision, ask a targeted question, and move.
- Restate crisply: “Given X, return Y under constraints Z.”
- Clarify constraints: input size, value ranges, sorting guarantees, uniqueness, mutability, multiple answers allowed, return any vs return all.
- Build examples: use the provided one; add one of your own that stresses the rules.
- Edge Case Menu (pick what fits):
- empty input, single element
- duplicates, all equal
- negatives/zeroes/overflow
- sorted vs reverse
- boundaries: min/max sizes
- graph/tree degenerates: empty, single node, disconnected
- invalid input policy (if relevant)
- Write it down: capture decisions as short bullet comments. Interviewers can’t see your scratch paper—your comments are your notes.
- Start with brute force to anchor correctness. Then beat it.
- Reason formally: estimate time and space before you code.
- Kill broken ideas early: try to break your own plan with targeted tests.
- Don’t data-structure shop. Explain why your chosen structure solves the bottleneck.
- Talk track template:
- “Naive approach is … complexity …”
- “We can improve by using … because …”
- “Chosen plan: … Steps: … Complexity: … Edge cases handled by …”
- “I’ll implement if that sounds good.”
- Small increments: write a function, test mentally, then extend.
- Name well:
target_idx
,seen
,window_start
beatsi
,arr2
. - Comment lightly: section headers and invariants; avoid narrating obvious syntax.
- Handle realizations: if a discovery changes architecture, pause and adjust; otherwise leave a TODO comment and finish the path.
- Syntax gaps: ask to check official docs for exact method names if needed.
- Three tests: happy path, edge case, stress-flavored case.
- Trace once out loud: variable values, branch decisions.
- State complexity: time and space, and why.
- Offer upgrades: in-place, reduced allocations, better DS choice, pre-sorting if allowed, memoization. Mention trade-offs.
Given an array nums
and integer target
, return indices of two distinct elements whose sum equals target
. Return any valid pair. Assume exactly one solution exists. Do not reuse the same element.
- Inputs:
nums
length n (possibly large), values can be negative or zero. - Output: two indices
[i, j]
withi != j
. - Order: any order is acceptable unless specified.
- Edge cases to confirm: duplicates allowed, multiple answers exist but any is fine, indices not values.
- Brute force: check all pairs. Time
O(n^2)
, spaceO(1)
. Always correct. - Hash map (chosen): one pass; for each
x
, check iftarget - x
seen earlier. TimeO(n)
, spaceO(n)
. Handles negatives and duplicates cleanly. - Sorted two-pointer: only if returning values or if we can return original indices after keeping pairs; requires care to track original indices and changes complexity to
O(n log n)
due to sorting.
Chosen plan: one-pass hash map from value to index.
def two_sum(nums, target):
"""
Return indices i, j such that nums[i] + nums[j] == target.
One-pass hash map: seen[value] = index of value observed so far.
Time: O(n), Space: O(n)
"""
seen = {}
for j, x in enumerate(nums):
need = target - x
if need in seen: # found partner seen earlier
return [seen[need], j]
seen[x] = j # record current index for future partners
return [] # if no solution policy changed, otherwise raise
#include <vector>
#include <unordered_map>
using namespace std;
vector<int> twoSum(const vector<int>& nums, int target) {
unordered_map<int,int> seen; // value -> index
for (int j = 0; j < (int)nums.size(); ++j) {
int need = target - nums[j];
auto it = seen.find(need);
if (it != seen.end()) return {it->second, j};
seen[nums[j]] = j;
}
return {};
}
- Tests:
nums=[2,7,11,15], target=9 -> [0,1]
nums=[3,3], target=6 -> [0,1]
(duplicates)nums=[-1,4,5], target=4 -> [0,2]
(negative + positive)
- Complexity:
O(n)
time,O(n)
space. One pass, handles duplicates, negatives, and large inputs.
- Return the values instead of indices: return
[need, x]
. - Return all pairs: use counts or a two-pass map strategy.
- Input guaranteed sorted: use two pointers in
O(1)
extra space.
- Misreading the spec: restate and confirm. Many misses begin here.
- Overfitting to examples: create a second example that stresses edge rules.
- Conditional sprawl: endless if-else patches usually signal a flawed core idea.
- Silence: narrate high-level thinking. Ten seconds of silence feels long.
- Stuck around minute 12: summarize options, pick a path, justify it, and move.
Sample script for targeted guidance:
I’ve considered:
• A: … (complexity, counterexample)
• B: … (complexity, counterexample)
• C: … (trade-offs)Given time, I’ll implement B because it handles large n and duplicates cleanly.
Does that sound reasonable?
- Core set: NeetCode 150 or a balanced Pareto set across arrays, maps/sets, two pointers, sliding window, stacks, binary search, trees/graphs (BFS/DFS), heaps, DP.
- Weekly cadence (suggested):
- Mon–Fri: 1 timed problem/day (25 min + 10 min review).
- Sat: 2 mock interviews with a partner. Swap roles.
- Sun: Review log, categorize misses, reattempt the two hardest.
- Deliberate drills:
- Understand sprints (5 min): restate, constraints, edge list only.
- Pattern tagging: after each solve, write “pattern + invariant” in one line.
- Complexity first: state
T(n), S(n)
before writing code.
Track in your repo. Small, consistent reps beat marathon crams.
- Restated the problem and confirmed constraints.
- Listed relevant edge cases and recorded decisions in comments.
- Explored brute force, then picked a stronger plan with trade-offs.
- Implemented in small, readable steps with clear names.
- Walked through three tests out loud and adjusted if needed.
- Reported time and space complexity.
- Proposed at least one optimization or alternative.
Hit this consistently and you’re operating above the bar.
Common Patterns
- Two pointers — sorted arrays, window boundaries
- Sliding window — substring/subarray with constraints
- Hash map/set — membership, counts, complements
- Stack/monotonic — next greater, valid parentheses
- BFS/DFS — shortest path (unweighted), components
- Binary search — on arrays or on “answer space”
- DP — overlapping subproblems with a clear recurrence
Allowed Lookups (confirm with interviewer)
- Official language documentation for syntax only.
Not Allowed
- AI assistants, solution searches, complexity lookups for library methods.
Written and maintained by PROGSU. Contributions welcome—open a PR with clear diffs and rationale.