← Home

10 LeetCode · Complexity Reference

FE/BE Deep →

// drill 2026-05-19 — interview hôm nay 15:00

10 LeetCode — Complexity Drill Reference

Time + Space + Reasoning + Saturday's mistakes corrected · Print-friendly

⚡ Quick Reference (memorize)

# Problem Pattern Time Space
1 Two Sum Hashmap O(n) O(n)
2 Contains Duplicate Set O(n) O(n)
3 Valid Anagram Map balance ±1 O(n) O(k) ASCII→O(1)
4 Group Anagrams Bucketing O(n · k log k) O(n · k)
5 3Sum Sort + Two Pointers O(n²) O(1) excl. output
6 Longest Substring No Repeat Sliding Window O(n) amortized O(min(n, k))
7 Valid Parentheses Stack (LIFO) O(n) O(n) KHÔNG O(1)
8 Reverse Linked List 3-pointer iterative O(n) O(1) iter / O(n) rec
9 Max Depth Binary Tree DFS recursion O(n) O(h) log n bal · n skew
10 Climbing Stairs DP bottom-up O(n) O(1) 2-var / O(n) array

🔴 Rows highlighted = bài Saturday speed re-solve sai complexity. Drill kỹ.

📚 Chi tiết từng bài

1. Two Sum · Hashmap one-pass

Easy
function twoSum(nums: number[], target: number): number[] {
  const seenIndex = new Map<number, number>();
  for (let i = 0; i < nums.length; i++) {
    const complement = target - nums[i];
    if (seenIndex.has(complement)) return [seenIndex.get(complement)!, i];
    seenIndex.set(nums[i], i);
  }
  return [];
}
Time O(n) · one pass, mỗi op Map là O(1) avg
Space O(n) · Map worst case giữ n entries

Sat note: Quên TS ! non-null assertion sau .get(). Naming sum → đổi seenIndex.

🧠 Brainstorm cue: "Find pair satisfying sum/equation?" → hashmap complement. Walk array, for each x check "have I seen target - x?" Insert after check (tránh dùng cùng index).

🔗 Similar LC: Two Sum II Sorted (#167) · 3Sum (#15) · 4Sum (#18) · Two Sum III Design (#170)

2. Contains Duplicate · Set uniqueness

Easy
function containsDuplicate(nums: number[]): boolean {
  return new Set(nums).size !== nums.length;
}
Time O(n) · Set construction iterates once
Space O(n) · Set worst case (all unique)

Bonus optimize: Early-exit version (return true ngay khi gặp dup) — best-case O(1) space dù worst case vẫn O(n).

🧠 Brainstorm cue: "Uniqueness / membership check?" → Set. O(1) membership lookup. new Set(arr).size !== arr.length = idiom.

🔗 Similar LC: Contains Duplicate II (#219) · Happy Number (#202) · Intersection of Two Arrays (#349) · Single Number (#136)

3. Valid Anagram · Map balance ±1

Easy
function isAnagram(s: string, t: string): boolean {
  if (s.length !== t.length) return false;
  const freq = new Map<string, number>();
  for (let i = 0; i < s.length; i++) {
    freq.set(s[i], (freq.get(s[i]) ?? 0) + 1);
    freq.set(t[i], (freq.get(t[i]) ?? 0) - 1);
  }
  for (const count of freq.values()) if (count !== 0) return false;
  return true;
}
Time O(n) · single loop n iterations
Space O(k) · k = unique chars; ASCII → O(1) bounded, lowercase a-z → O(26)

Verbalize: "If input is lowercase a-z, k ≤ 26 → effectively O(1) space."

🧠 Brainstorm cue: "Compare 2 collections for equivalence?" → 2 cách: (a) Map ±1 balance (1 loop, 1 map), (b) Sort cả 2 và so sánh. Map balance đẹp hơn cho interview vì O(n) vs O(n log n).

🔗 Similar LC: Group Anagrams (#49) · Find All Anagrams in String (#438) · Permutation in String (#567) · First Unique Character (#387)

4. Group Anagrams · Bucketing 🚨 Sat MISTAKE

Medium
function groupAnagrams(strs: string[]): string[][] {
  const buckets = new Map<string, string[]>();
  for (const str of strs) {
    const sortedKey = [...str].sort().join('');
    if (!buckets.has(sortedKey)) buckets.set(sortedKey, []);
    buckets.get(sortedKey)!.push(str);
  }
  return Array.from(buckets.values());
}
Time O(n · k log k) · n strings × k log k sort cost
Space O(n · k) · buckets giữ n strings, mỗi cái độ dài k

Sat mistake: Nói O(n) / O(k) — THIẾU k và n. Bài 2 chiều phải có 2 biến.

Cụm từ: "n strings × per-string-sort-cost. Comparison-based sort = O(k log k). Total time O(n · k log k). Space O(n · k) vì buckets giữ tất cả n × k chars."

Optimize nếu a-z: Count-key thay sort → O(n · k) time.

🧠 Brainstorm cue: "Group items by computed property?" → Bucketing pattern: Map<fingerprint, T[]>. Compute key, lazy-init bucket, push item. Cuối cùng Array.from(map.values()).

🔗 Similar LC: Group Shifted Strings (#249) · Top K Frequent Elements (#347) · Sort Characters By Frequency (#451)

5. 3Sum · Sort + Two Pointers + 3 dedup

Medium
function threeSum(nums: number[]): number[][] {
  const result: number[][] = [];
  nums.sort((a, b) => a - b);
  for (let i = 0; i < nums.length - 2; i++) {
    if (nums[i] > 0) break;
    if (i > 0 && nums[i] === nums[i - 1]) continue;
    let left = i + 1, right = nums.length - 1;
    while (left < right) {
      const sum = nums[i] + nums[left] + nums[right];
      if (sum < 0) left++;
      else if (sum > 0) right--;
      else {
        result.push([nums[i], nums[left], nums[right]]);
        while (left < right && nums[left] === nums[left + 1]) left++;
        while (left < right && nums[right] === nums[right - 1]) right--;
        left++; right--;
      }
    }
  }
  return result;
}
Time O(n²) · sort O(n log n) + outer × inner two-pointer = O(n²)
Space O(1) · in-place sort, excluding output

3SUM-hardness: O(n²) là practical floor. Sub-quadratic exists nhưng không practical.

Sat note: Code đẹp nhưng quên state complexity bằng lời.

🧠 Brainstorm cue: "Find k-tuple summing to target?" → Sort + Two Pointers. Sort unlock monotonic decision (sum < 0 → L++, sum > 0 → R--). Outer fix 1 element, inner 2-pointer scan rest. 3 dedup layers: outer i, left after hit, right after hit.

🔗 Similar LC: 2Sum II Sorted (#167) · 3Sum Closest (#16) · 4Sum (#18) · Container With Most Water (#11) · Trapping Rain Water (#42)

6. Longest Substring No Repeat · Sliding Window 🚨 Sat MISTAKE

Medium
function lengthOfLongestSubstring(s: string): number {
  const windowChars = new Set<string>();
  let left = 0, res = 0;
  for (let right = 0; right < s.length; right++) {
    const currentChar = s[right];
    while (windowChars.has(currentChar)) {
      windowChars.delete(s[left]);
      left++;
    }
    windowChars.add(currentChar);
    res = Math.max(res, right - left + 1);
  }
  return res;
}
Time O(n) AMORTIZED · left pointer monotonic, each char touched ≤ 2×
Space O(min(n, k)) · k = charset; ASCII → O(128) bounded

Sat mistake: Nói O(n²) vì thấy nested while trong for. SAI — pointer monotonic → amortized O(n).

Cụm từ memorize: "Despite nested while in for, left pointer monotonic — only moves forward. Total work = O(n) amortized. Each char touched at most 2 times."

🧠 Brainstorm cue: "Longest/shortest contiguous segment with property X?" → Sliding Window. Right expand, shrink left khi vi phạm constraint. State trong Set/Map track window content. Amortized O(n) khi pointer monotonic.

🔗 Similar LC: Longest Substring K Distinct (#340) · Minimum Window Substring (#76) · Longest Repeating Char Replacement (#424) · Permutation in String (#567)

7. Valid Parentheses · Stack LIFO 🚨 Sat BUG + MISTAKE

Easy
function isValid(s: string): boolean {
  const pairs = new Map<string, string>([
    [')', '('], [']', '['], ['}', '{'],
  ]);
  const openStack: string[] = [];
  for (const char of s) {
    if (pairs.has(char)) {
      if (openStack.pop() !== pairs.get(char)) return false;  // ← () critical!
    } else {
      openStack.push(char);
    }
  }
  return openStack.length === 0;
}
Time O(n) · single pass
Space O(n) · worst case "((((..." stack giữ n openers

Sat bug: openStack.pop thiếu () → function reference, không gọi → mọi closing bracket return false. Fixed Mon AM.

Sat space mistake: Nói O(1) — SAI. Stack có thể giữ n items → O(n).

🧠 Brainstorm cue: "Nested/matching structures?" → Stack LIFO. Push opens, pop on close + verify match. Stack rỗng cuối = OK. Map <closer, opener> cho fast lookup.

🔗 Similar LC: Generate Parentheses (#22) · Longest Valid Parentheses (#32) · Min Add to Make Valid (#921) · Decode String (#394) · Eval Reverse Polish (#150)

8. Reverse Linked List · 3-pointer iterative

Easy
function reverseList(head: ListNode | null): ListNode | null {
  let prev: ListNode | null = null;
  let curr: ListNode | null = head;
  while (curr) {
    const nextTemp = curr.next;
    curr.next = prev;
    prev = curr;
    curr = nextTemp;
  }
  return prev;
}
Time O(n) · visit mỗi node 1 lần
Space O(1) · 3 pointers cố định

Recursive version: Same O(n) time, BUT O(n) space cho call stack. Mention if asked.

🧠 Brainstorm cue: "Modify linked list structure?" → Save reference TRƯỚC khi flip. 3-pointer pattern: prev, curr, nextTemp. 4 bước/iteration: (1) save next, (2) flip curr.next, (3) advance prev, (4) advance curr.

🔗 Similar LC: Reverse Linked List II (#92) · Reverse Nodes in K-Group (#25) · Swap Pairs (#24) · Palindrome Linked List (#234) · Reorder List (#143)

9. Max Depth Binary Tree · DFS 🚨 Sat MISTAKE

Easy
function maxDepth(root: TreeNode | null): number {
  if (root === null) return 0;
  return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
Time O(n) · visit mỗi node 1 lần
Space O(h) · h = height. Balanced O(log n), Skewed O(n) — call stack depth

Sat mistake: Nói O(n²) — SAI. Space của tree DFS phụ thuộc HEIGHT, không phải n × n.

Cụm từ: "Time O(n) — visit each node once. Space O(h) — h is tree height. Balanced: O(log n). Skewed: O(n)."

🧠 Brainstorm cue: "Tree property phụ thuộc subtree results?" → DFS recursion. 2 câu hỏi: (1) Base case? (thường null), (2) Recursive case: ghép kết quả của left + right thế nào? Stack ngầm tự nhớ đường về.

🔗 Similar LC: Same Tree (#100) · Symmetric Tree (#101) · Invert Binary Tree (#226) · Diameter (#543) · Balanced (#110) · Path Sum (#112)

10. Climbing Stairs · DP bottom-up

Easy
function climbStairs(n: number): number {
  if (n <= 2) return n;
  let prev2 = 1, prev1 = 2;
  for (let i = 3; i <= n; i++) {
    const current = prev1 + prev2;
    prev2 = prev1;
    prev1 = current;
  }
  return prev1;
}
Time O(n) · single loop từ 3 đến n
Space O(1) · 2 biến cố định (không phải DP array)

Why DP not just Fibonacci? "Fibonacci là RESULT, không phải REASONING. Recurrence: dp[i] = dp[i-1] + dp[i-2] vì bước cuối là 1 hoặc 2 bậc → mutually exclusive cases, cộng lại."

Matrix expo: "Có thể tụt xuống O(log n) time bằng matrix exponentiation — nhưng overkill cho interview."

🧠 Brainstorm cue: "Đếm số cách đạt state X?" hoặc "Min/max value achievable?" → DP. 4 bước: (1) Define state dp[i], (2) Recurrence (bước cuối là gì?), (3) Base case, (4) Direction (bottom-up tabulation hoặc top-down memo). Space optimize nếu chỉ phụ thuộc k giá trị trước.

🔗 Similar LC: Min Cost Climbing Stairs (#746) · House Robber (#198) · Fibonacci Number (#509) · Decode Ways (#91) · Unique Paths (#62)

🎤 Complexity Articulation Formula

Sau MỌI solve, đọc CÂU này ra mồm:

"Time O(?) because [iteration shape].
Space O(?) because [auxiliary structure size]."

Examples:

⚠️ 4 Complexity Traps (sai chéo Sat)

Trap 1: Nested while → O(n²)

Nested while trong for KHÔNG tự động O(n²). Nếu inner pointer monotonic (only forward) → amortized O(n).

Apply: Sliding Window, Two Pointers cùng chiều, Monotonic Stack/Queue

Trap 2: Tree DFS space = O(n²)

SAI — tree DFS space là O(h) call stack depth. Balanced O(log n), Skewed O(n). Không có chỗ nào tăng theo n × n.

Apply: Mọi tree recursion

Trap 3: Stack iterative = O(1)

SAI — stack size phụ thuộc input. Worst case "(((..." stack giữ n items → O(n).

Apply: Valid Parens, Iterative tree traversal

Trap 4: 1 biến cho collection

Input là collection of items → complexity PHẢI có 2+ biến. Matrix m × n → O(m·n). N strings có k chars → O(n·k).

Apply: Group Anagrams, Matrix problems, list of lists

🚨 Live Coding Survival Guide

⚠️ Reality: Senior FE devs cũng quên syntax DSA. Production code không cần memorize Array.from(new Map().entries()). Interviewer biết điều này.

✅ Mindset: "Tôi nhớ core 15 syntax. Quên cái khác = OK, tôi pseudocode hoặc ask. Tech lead expect this."

🧠 Pattern Recognition — Đọc đề trong 30 giây

Bước ĐẦU TIÊN trong mọi DSA interview: đọc đề → match keywords → recognize pattern. Trước khi code, BẮT BUỘC làm step này.

Pattern Trigger Keywords (đề bài) DS / Algo Reference
Hashmap "find pair sum to target", "duplicate", "frequency", "complement", "any two ___" Map<K,V> Two Sum · Anagram
Set (uniqueness) "contains duplicate?", "all unique?", "intersection", "membership" Set<T> Contains Duplicate
Bucketing "group by ___", "categorize", "anagram groups" Map<key, T[]> Group Anagrams
Two Pointers "sorted" array · "pair sum", "palindrome", "reverse in place", "k-sum" Sort + L/R pointers 3Sum
Sliding Window "contiguous" · "longest/shortest substring/subarray", "max sum window" L/R window + Set/Map Longest Substring
Stack (LIFO) "matching brackets/tags", "nested", "next greater/smaller", "valid expression" arr.push/pop Valid Parens
Queue (FIFO) / BFS "level-order", "shortest path", "minimum steps", "spread/infect" Array + shift/push — (related to Tree DFS)
Linked List "reverse", "merge two lists", "cycle detection", "k-th from end" 3-pointer or fast/slow Reverse LL
Tree DFS "binary tree", "depth", "path sum", "tree property", "subtree" Recursion (left, right) Max Depth Tree
Tree BFS "level-order traversal", "rightmost view", "minimum depth" Queue + per-level
Dynamic Programming "count ways", "max/min", "subsequence", "can we reach?", "fewest ___" dp[] array / 2-var Climbing Stairs
Recursion / Backtracking "smaller subproblem", "divide", "generate all", "permutations/combinations" Recursive + undo state
Binary Search "sorted" · "find target", "first/last position", "search in rotated", "find peak" L/R mid pointers
Greedy "max profit", "minimum coins/jumps", "interval scheduling", "local choice" Local optimal pick
Prefix Sum "subarray sum equals K", "range sum query", "cumulative" Build sum[] + Map
🎯 Cách dùng table trên trong 30 giây đầu
  1. Đọc đề 2 lần — gạch chân (mental) các keyword
  2. Match keywords → row trong table → recognize pattern
  3. Verbalize: "Em thấy keywords [X, Y] → đây là [pattern name] problem. Em sẽ approach bằng [DS/algo]."
  4. Confirm với interviewer: "Anh/chị xác nhận direction này có hợp lý không?"

Critical insight: Interviewer chấm điểm process nhiều hơn answer. Step này show "anh này đã làm DSA nhiều, biết cách phân loại bài."

💡 Khi đề có MULTIPLE signals

Một số bài combine 2 patterns. Ví dụ:

→ Nếu thấy 2 signals → mention cả 2 trong verbalize. "Em thấy đây là combo của X và Y."

📚 15 TS Syntax Must-Know (đủ solve 80% Easy/Medium)

Array (5)
const arr: number[] = [];
arr.push(x);  arr.pop();
arr.length;   arr[i];
arr.sort((a, b) => a - b);   // numbers!

// Iterate
for (let i = 0; i < arr.length; i++) { }
for (const x of arr) { }
Map (5)
const map = new Map<K, V>();
map.set(key, value);
map.get(key);     // returns V | undefined
map.has(key);     // boolean
map.delete(key);

// Iterate
for (const [k, v] of map) { }
for (const v of map.values()) { }
Array.from(map.values())
Set (3)
const set = new Set<T>();
set.add(x);
set.has(x);       // boolean
set.delete(x);
set.size;

// Common idioms
new Set(arr).size !== arr.length  // dup check
[...new Set(arr)]                  // dedup
String + Loops (2)
str.length;     str[i];     // char access
[...str]                    // → array of chars
str.split('')               // alternative
chars.join('')              // array → string

// Sort string
[...str].sort().join('')

// Common: charCodeAt - 'a'
s.charCodeAt(0) - 97

🆘 3 Fallback Channels khi forget syntax

1. ASK interviewer

"Dạ syntax của Map iteration trong TS em hơi quên, anh có thể remind em không ạ?"

Tech lead có nghề welcome. Show honesty + flexibility. KHÔNG mất điểm.

2. PSEUDOCODE trước
// for each i in nums:
//   complement = target - nums[i]
//   if complement is in map:
//     return [map[complement], i]
//   set map[nums[i]] = i

Confirm logic OK trước → fill syntax sau. Adjust nếu sai.

3. COMMENT honestly
if (map.has(complement)) {
  // sorry, quên exact method
  return [map.get(complement)!, i];
}

Comment honest về uncertainty = professional. Tech lead có thể giúp.

💬 3 Killer Phrases (memorize)

PHRASE 1 · Stuck thinking

"Dạ em đang nghĩ — em thấy bài này có signal là [sorted array / find pair], nên em đang cân nhắc giữa [pattern A] và [pattern B]. Em thử approach [A] trước với brute force, sau đó optimize nếu còn thời gian. Anh/chị thấy có sense không ạ?"

→ Show thinking process = senior signal. Interviewer thường confirm direction hoặc hint nhẹ.

PHRASE 2 · Brute force first

"Dạ em sẽ làm brute force trước với O(n²). Sau đó nếu còn thời gian em sẽ optimize bằng [hashmap / sliding window / sort+two pointers]."

→ Brute force submitted > nothing submitted. Mỗi problem đều có naive solution.

PHRASE 3 · Ask for hint (sau 5-7 phút stuck)

"Dạ anh/chị có thể cho em một hint nhỏ về data structure phù hợp không ạ?"

→ KHÔNG mất điểm. Show: escalate khi stuck, communication, time management.

🚫 TUYỆT ĐỐI KHÔNG xin dùng AI

Lý do: Live coding test SKILL, không phải tool. Xin dùng Claude/GPT = defeats entire purpose. 100% downside, 0% upside.

Bro nghĩTech Lead nhận
"Tôi practical, biết dùng tool""Anh này không tự code được"
"Tôi adapt với modern workflow""Anh này dependence vào AI"
"Show modern AI usage""Production code AI-generated → unmaintainable"

Exception: Nếu tech lead OFFER (rất hiếm) — OK dùng cho lookup syntax / verify approach. NEVER dump full problem.

🔀 Switch language nếu cần

Nếu TS types stress quá, switch JS — KHÔNG mất điểm.

"Dạ em có thể code bằng JavaScript thay vì TypeScript không ạ? Em mạnh JS hơn, focus logic dễ hơn."

LanguageProsCons
TypeScriptShow modern stackType errors có thể distract
JavaScriptCleaner syntax, focus logicShow ít TS knowledge

📝 Notepad Strategy (bring physical paper)

Mang notepad vào interview. Đây không phải cheat — engineer workflow reality. Tech lead OK.

Cấu trúc notepad:

PAGE 1: Personal note
- Tên interviewers (ghi khi họ giới thiệu)
- 3 questions hỏi back cuối interview

PAGE 2: TS Syntax mini cheatsheet (viết tay 15 syntax trên)

PAGE 3-5: Để trống cho take notes lúc PV

⚠️ KHÔNG: Mang printed LeetCode solutions. CHỈ syntax reference + personal notes.

🛡️ Edge Cases — Verbal Walkthrough

Không cần solve hết edge cases. Mention ra miệng = đủ — show defensive thinking.

"Dạ edge cases em sẽ check: empty input · single element · duplicates · negative numbers · very large input."

Standard checklist applies cho hầu hết problems. Memo + bring up đầu interview.