Buổi 13 — Final Mock: FAANG Mastery Complete

Navigation

12-Practice-FAANG-1 | → Roadmap-FAANG Giai đoạn: FAANG Mastery | Loại: Final Assessment


1. Final Assessment Overview

Đây là buổi culminating session — mô phỏng hoàn chỉnh một FAANG on-site loop thực tế. Tại các công ty như Google, Meta, Amazon: một on-site loop thường gồm 4-5 interview rounds kéo dài cả ngày. Buổi này simulate 3 rounds chính.

Cách dùng buổi này hiệu quả nhất

Option A — Full Simulation: Đặt timer 45 phút/round, tắt solutions, làm như interview thật. Sau đó mới mở xem. Option B — Study Mode: Đọc problem, tự nghĩ approach 10 phút, rồi đọc solution và phân tích. Recommend: làm Option A ít nhất 1 lần trước ngày phỏng vấn thật.

Cấu trúc buổi:

RoundFocusProblemThời gian
Round 1Coding Interview#295 Find Median from Data Stream45 phút
Round 2Algorithm Design#23 Merge K Sorted Lists45 phút
Round 3System-Level Algorithm#297 Serialize and Deserialize Binary Tree45 phút

2. Round 1 — Coding Interview (45 phút)

Problem: #295 Find Median from Data Stream (Hard)

Đề bài: Design một data structure hỗ trợ 2 operations:

  • addNum(num): Thêm số nguyên num từ data stream
  • findMedian(): Trả về median của tất cả số đã thêm

Ví dụ:

addNum(1)   → stream: [1]        → median = 1.0
addNum(2)   → stream: [1, 2]     → median = 1.5
addNum(3)   → stream: [1, 2, 3]  → median = 2.0
addNum(4)   → stream: [1,2,3,4]  → median = 2.5

Clarifying questions:

  • Số lượng addNum calls tối đa? → Lên đến 5 × 10⁴
  • Có số âm không? → Có, trong range [-10⁵, 10⁵]
  • findMedian được gọi bao nhiêu lần? → Lên đến 5 × 10⁴

Key insight — Tại sao dùng 2 Heaps?

  • Median là phần tử “ở giữa” của dãy đã sort
  • maxHeap (lower half): chứa nửa nhỏ, root = phần tử lớn nhất của nửa nhỏ
  • minHeap (upper half): chứa nửa lớn, root = phần tử nhỏ nhất của nửa lớn
  • Median = root của maxHeap (nếu lẻ) hoặc trung bình 2 roots (nếu chẵn)
// MedianFinder — Two Heaps approach
// addNum: O(log N) | findMedian: O(1)
// Space: O(N)
 
// TypeScript không có built-in Heap, phải implement MinHeap và MaxHeap
class MinHeap {
  private data: number[] = [];
 
  push(val: number): void {
    this.data.push(val);
    this.bubbleUp(this.data.length - 1);
  }
 
  pop(): number {
    if (this.data.length === 1) return this.data.pop()!;
    const top = this.data[0];
    this.data[0] = this.data.pop()!;
    this.sinkDown(0);
    return top;
  }
 
  peek(): number {
    return this.data[0];
  }
 
  size(): number {
    return this.data.length;
  }
 
  private bubbleUp(i: number): void {
    while (i > 0) {
      const parent = Math.floor((i - 1) / 2);
      if (this.data[parent] <= this.data[i]) break;
      [this.data[parent], this.data[i]] = [this.data[i], this.data[parent]];
      i = parent;
    }
  }
 
  private sinkDown(i: number): void {
    const n = this.data.length;
    while (true) {
      let smallest = i;
      const left = 2 * i + 1;
      const right = 2 * i + 2;
      if (left < n && this.data[left] < this.data[smallest]) smallest = left;
      if (right < n && this.data[right] < this.data[smallest]) smallest = right;
      if (smallest === i) break;
      [this.data[smallest], this.data[i]] = [this.data[i], this.data[smallest]];
      i = smallest;
    }
  }
}
 
class MaxHeap {
  private data: number[] = [];
 
  push(val: number): void {
    this.data.push(val);
    this.bubbleUp(this.data.length - 1);
  }
 
  pop(): number {
    if (this.data.length === 1) return this.data.pop()!;
    const top = this.data[0];
    this.data[0] = this.data.pop()!;
    this.sinkDown(0);
    return top;
  }
 
  peek(): number {
    return this.data[0];
  }
 
  size(): number {
    return this.data.length;
  }
 
  private bubbleUp(i: number): void {
    while (i > 0) {
      const parent = Math.floor((i - 1) / 2);
      if (this.data[parent] >= this.data[i]) break;
      [this.data[parent], this.data[i]] = [this.data[i], this.data[parent]];
      i = parent;
    }
  }
 
  private sinkDown(i: number): void {
    const n = this.data.length;
    while (true) {
      let largest = i;
      const left = 2 * i + 1;
      const right = 2 * i + 2;
      if (left < n && this.data[left] > this.data[largest]) largest = left;
      if (right < n && this.data[right] > this.data[largest]) largest = right;
      if (largest === i) break;
      [this.data[largest], this.data[i]] = [this.data[i], this.data[largest]];
      i = largest;
    }
  }
}
 
class MedianFinder {
  // maxHeap: nửa nhỏ hơn (lower half), root = max của nửa này
  private lowerHalf: MaxHeap;
  // minHeap: nửa lớn hơn (upper half), root = min của nửa này
  private upperHalf: MinHeap;
 
  constructor() {
    this.lowerHalf = new MaxHeap();
    this.upperHalf = new MinHeap();
  }
 
  addNum(num: number): void {
    // Bước 1: Thêm vào lowerHalf trước
    this.lowerHalf.push(num);
 
    // Bước 2: Đảm bảo tất cả phần tử ở lowerHalf ≤ tất cả phần tử ở upperHalf
    // Nếu max(lowerHalf) > min(upperHalf) → vi phạm invariant → rebalance
    if (
      this.upperHalf.size() > 0 &&
      this.lowerHalf.peek() > this.upperHalf.peek()
    ) {
      this.upperHalf.push(this.lowerHalf.pop());
    }
 
    // Bước 3: Balance kích thước — lowerHalf được phép có nhiều hơn 1 phần tử
    // (khi tổng số phần tử lẻ, lowerHalf chứa median)
    if (this.lowerHalf.size() > this.upperHalf.size() + 1) {
      this.upperHalf.push(this.lowerHalf.pop());
    } else if (this.upperHalf.size() > this.lowerHalf.size()) {
      this.lowerHalf.push(this.upperHalf.pop());
    }
  }
 
  findMedian(): number {
    // Tổng số phần tử lẻ → median là root của lowerHalf
    if (this.lowerHalf.size() > this.upperHalf.size()) {
      return this.lowerHalf.peek();
    }
    // Tổng số phần tử chẵn → median là trung bình 2 roots
    return (this.lowerHalf.peek() + this.upperHalf.peek()) / 2;
  }
}
 
// Test
const medianFinder = new MedianFinder();
medianFinder.addNum(1);
console.log(medianFinder.findMedian()); // 1.0
medianFinder.addNum(2);
console.log(medianFinder.findMedian()); // 1.5
medianFinder.addNum(3);
console.log(medianFinder.findMedian()); // 2.0
medianFinder.addNum(4);
console.log(medianFinder.findMedian()); // 2.5

Complexity Analysis:

OperationTimeSpace
addNumO(log N)
findMedianO(1)
Overall SpaceO(N)

Follow-up questions thường gặp

Q: Nếu số liệu lệch nhiều (99% < 100, 1% > 1000000), có optimize được không? A: Dùng Bucketing hoặc Order Statistics Tree (Red-Black Tree). Trong phỏng vấn, mention được đây là follow-up quan trọng.


3. Round 2 — Algorithm Design (45 phút)

Problem: #23 Merge K Sorted Lists (Hard)

Đề bài: Cho mảng lists gồm K linked lists đã sort tăng dần. Merge tất cả thành 1 linked list đã sort.

Ví dụ:

Input: lists = [[1,4,5], [1,3,4], [2,6]]
Output: [1, 1, 2, 3, 4, 4, 5, 6]

Clarifying questions:

  • K có thể là 0 (empty array) không? → Có, trả về null
  • Lists có thể có linked list rỗng không? → Có
  • Tổng số nodes N là bao nhiêu? → N ≤ 10⁴

Approach 1: Priority Queue — O(N log K)

class ListNode {
  val: number;
  next: ListNode | null;
  constructor(val = 0, next: ListNode | null = null) {
    this.val = val;
    this.next = next;
  }
}
 
// Priority Queue (Min-Heap) cho ListNode
// So sánh theo val của node
class NodeMinHeap {
  private data: ListNode[] = [];
 
  push(node: ListNode): void {
    this.data.push(node);
    this.bubbleUp(this.data.length - 1);
  }
 
  pop(): ListNode {
    if (this.data.length === 1) return this.data.pop()!;
    const top = this.data[0];
    this.data[0] = this.data.pop()!;
    this.sinkDown(0);
    return top;
  }
 
  peek(): ListNode {
    return this.data[0];
  }
 
  size(): number {
    return this.data.length;
  }
 
  private bubbleUp(i: number): void {
    while (i > 0) {
      const parent = Math.floor((i - 1) / 2);
      if (this.data[parent].val <= this.data[i].val) break;
      [this.data[parent], this.data[i]] = [this.data[i], this.data[parent]];
      i = parent;
    }
  }
 
  private sinkDown(i: number): void {
    const n = this.data.length;
    while (true) {
      let smallest = i;
      const left = 2 * i + 1;
      const right = 2 * i + 2;
      if (left < n && this.data[left].val < this.data[smallest].val) smallest = left;
      if (right < n && this.data[right].val < this.data[smallest].val) smallest = right;
      if (smallest === i) break;
      [this.data[smallest], this.data[i]] = [this.data[i], this.data[smallest]];
      i = smallest;
    }
  }
}
 
// Approach 1: Priority Queue
// Ý tưởng: duy trì heap gồm K nodes đang ở "đầu" của mỗi list
// Mỗi lần lấy min ra, thêm node tiếp theo của list đó vào heap
// Time: O(N log K) — mỗi node push/pop heap 1 lần, heap size luôn ≤ K
// Space: O(K) — heap chứa tối đa K nodes
function mergeKLists_Heap(lists: (ListNode | null)[]): ListNode | null {
  const heap = new NodeMinHeap();
  const dummy = new ListNode(0); // Sentinel node để đơn giản hóa code
  let current = dummy;
 
  // Thêm head của từng list vào heap (bỏ qua null nodes)
  for (const head of lists) {
    if (head !== null) heap.push(head);
  }
 
  while (heap.size() > 0) {
    // Lấy node nhỏ nhất từ heap
    const minNode = heap.pop();
    current.next = minNode;
    current = current.next;
 
    // Nếu node đó có next → thêm next vào heap
    if (minNode.next !== null) {
      heap.push(minNode.next);
    }
  }
 
  return dummy.next;
}

Approach 2: Divide & Conquer — O(N log K)

// Approach 2: Divide & Conquer
// Ý tưởng: merge từng cặp list lại → giảm K lists xuống K/2 lists → lặp lại
// Giống merge sort: log K rounds, mỗi round merge tổng N nodes
// Time: O(N log K) | Space: O(log K) cho call stack
function mergeKLists_DivideConquer(lists: (ListNode | null)[]): ListNode | null {
  if (lists.length === 0) return null;
 
  let interval = 1;
 
  // Mỗi vòng lặp: merge từng cặp lists[i] với lists[i + interval]
  while (interval < lists.length) {
    for (let i = 0; i + interval < lists.length; i += interval * 2) {
      lists[i] = mergeTwoLists(lists[i], lists[i + interval]);
    }
    interval *= 2;
  }
 
  return lists[0];
}
 
// Helper: merge 2 sorted linked lists (classic problem #21)
// Time: O(m + n) | Space: O(1)
function mergeTwoLists(
  l1: ListNode | null,
  l2: ListNode | null
): ListNode | null {
  const dummy = new ListNode(0);
  let current = dummy;
 
  while (l1 !== null && l2 !== null) {
    if (l1.val <= l2.val) {
      current.next = l1;
      l1 = l1.next;
    } else {
      current.next = l2;
      l2 = l2.next;
    }
    current = current.next;
  }
 
  // Nối phần còn lại (1 trong 2 list chưa hết)
  current.next = l1 ?? l2;
 
  return dummy.next;
}
 
// Helper để tạo linked list từ array (cho testing)
function arrayToList(arr: number[]): ListNode | null {
  const dummy = new ListNode(0);
  let cur = dummy;
  for (const v of arr) { cur.next = new ListNode(v); cur = cur.next; }
  return dummy.next;
}
 
// Helper để convert list sang array (cho testing)
function listToArray(head: ListNode | null): number[] {
  const res: number[] = [];
  while (head) { res.push(head.val); head = head.next; }
  return res;
}
 
// Test
const lists = [
  arrayToList([1, 4, 5]),
  arrayToList([1, 3, 4]),
  arrayToList([2, 6]),
];
console.log(listToArray(mergeKLists_Heap([...lists])));           // [1,1,2,3,4,4,5,6]
console.log(listToArray(mergeKLists_DivideConquer([...lists])));  // [1,1,2,3,4,4,5,6]

So sánh 2 approaches

Priority QueueDivide & Conquer
TimeO(N log K)O(N log K)
SpaceO(K) heapO(log K) stack
PracticalDễ code hơnElegant, no external heap
InterviewMention cả 2, code Heap trướcCode nếu còn thời gian
Tại sao cùng complexity? Cả 2 đều xử lý mỗi node log K lần. Heap approach: log K per heap op. D&C approach: log K merge rounds.

4. Round 3 — System-Level Algorithm (45 phút)

Problem: #297 Serialize and Deserialize Binary Tree (Hard)

Đề bài: Design thuật toán để:

  • serialize(root): Convert binary tree thành string
  • deserialize(data): Convert string lại thành binary tree

Ví dụ:

Input tree:    1
              / \
             2   3
                / \
               4   5

serialize → "1,2,null,null,3,4,null,null,5,null,null"  (ví dụ DFS)
deserialize → reconstruct lại tree ban đầu

Approach 1: BFS (Level-Order Serialization)

class TreeNode {
  val: number;
  left: TreeNode | null;
  right: TreeNode | null;
  constructor(val = 0, left: TreeNode | null = null, right: TreeNode | null = null) {
    this.val = val;
    this.left = left;
    this.right = right;
  }
}
 
// Approach 1: BFS — Level-order traversal
// Giống cách LeetCode hiển thị tree: "1,2,3,null,null,4,5"
// Time: O(N) | Space: O(N)
 
class BFSCodec {
  // Serialize: BFS, ghi từng node theo level order
  // null node ghi là "null"
  serialize(root: TreeNode | null): string {
    if (root === null) return "";
 
    const result: string[] = [];
    const queue: (TreeNode | null)[] = [root];
    let head = 0; // Index pointer — O(1) dequeue (xem 03-Stack-and-Queue § Pitfall 2)
 
    while (head < queue.length) {
      const node = queue[head++];
 
      if (node === null) {
        result.push("null");
      } else {
        result.push(String(node.val));
        // Thêm cả null children vào queue để serialize đầy đủ
        queue.push(node.left);
        queue.push(node.right);
      }
    }
 
    return result.join(",");
  }
 
  // Deserialize: đọc lần lượt, dùng queue để biết node nào đang cần children
  deserialize(data: string): TreeNode | null {
    if (data === "") return null;
 
    const values = data.split(",");
    const root = new TreeNode(parseInt(values[0]));
    const queue: TreeNode[] = [root];
    let head = 0; // Index pointer
    let i = 1;    // index trong values array
 
    while (head < queue.length && i < values.length) {
      const node = queue[head++];
 
      // Gán left child
      if (values[i] !== "null") {
        node.left = new TreeNode(parseInt(values[i]));
        queue.push(node.left);
      }
      i++;
 
      // Gán right child
      if (i < values.length && values[i] !== "null") {
        node.right = new TreeNode(parseInt(values[i]));
        queue.push(node.right);
      }
      i++;
    }
 
    return root;
  }
}

Approach 2: DFS (Preorder Serialization)

// Approach 2: DFS — Preorder traversal (root, left, right)
// Elegant recursive solution, dễ implement
// Time: O(N) | Space: O(N) — call stack O(H)
 
class DFSCodec {
  // Serialize: preorder DFS
  // Format: "1,2,null,null,3,4,null,null,5,null,null"
  serialize(root: TreeNode | null): string {
    const parts: string[] = [];
 
    function dfs(node: TreeNode | null): void {
      if (node === null) {
        parts.push("null");
        return;
      }
      parts.push(String(node.val)); // Pre-order: root trước
      dfs(node.left);               // rồi left
      dfs(node.right);              // rồi right
    }
 
    dfs(root);
    return parts.join(",");
  }
 
  // Deserialize: dùng index pointer (hoặc iterator) để đọc từng giá trị
  deserialize(data: string): TreeNode | null {
    const values = data.split(",");
    let index = 0; // pointer vào values array
 
    function buildTree(): TreeNode | null {
      if (index >= values.length || values[index] === "null") {
        index++; // consume "null" token
        return null;
      }
 
      const node = new TreeNode(parseInt(values[index]));
      index++; // consume current value
 
      node.left = buildTree();  // recursively build left subtree
      node.right = buildTree(); // recursively build right subtree
 
      return node;
    }
 
    return buildTree();
  }
}
 
// Test cả 2 approaches
function treeToArray(root: TreeNode | null): (number | null)[] {
  if (!root) return [];
  const res: (number | null)[] = [];
  const q: (TreeNode | null)[] = [root];
  let head = 0;
  while (head < q.length) {
    const n = q[head++];
    if (n === null) { res.push(null); continue; }
    res.push(n.val);
    q.push(n.left, n.right);
  }
  // trim trailing nulls
  while (res[res.length - 1] === null) res.pop();
  return res;
}
 
const tree = new TreeNode(1,
  new TreeNode(2),
  new TreeNode(3, new TreeNode(4), new TreeNode(5))
);
 
const bfsCodec = new BFSCodec();
const serializedBFS = bfsCodec.serialize(tree);
console.log("BFS serialized:", serializedBFS);
console.log("BFS deserialized:", treeToArray(bfsCodec.deserialize(serializedBFS)));
 
const dfsCodec = new DFSCodec();
const serializedDFS = dfsCodec.serialize(tree);
console.log("DFS serialized:", serializedDFS);
console.log("DFS deserialized:", treeToArray(dfsCodec.deserialize(serializedDFS)));

Khi nào dùng BFS vs DFS serialize?

  • BFS: dễ visualize, output giống LeetCode format, tốt cho “wide” trees
  • DFS Preorder: code ngắn gọn hơn, recursive elegant, tốt cho interview khi muốn impress
  • Cả 2 đều O(N) time/space — interviewer thường chấp nhận cả 2

5. Complete FAANG Readiness Checklist

Checklist này cover toàn bộ 25 buổi của vault. Tick khi cảm thấy tự tin giải được trong interview (không chỉ hiểu khi đọc).

Big Tech Foundation (Buổi 1-10)

  • Complexity Analysis: Phân tích được Time/Space cho bất kỳ algorithm nào, biết Master Theorem
  • Array & String: Prefix Sum, Kadane’s Algorithm, Two-pass scan, in-place manipulation
  • Linked List: Reverse, cycle detection (Floyd’s), merge, find middle (slow/fast pointers)
  • Stack & Queue: Monotonic stack, implement queue via stacks, expression evaluation
  • HashMap & HashSet: Frequency counting, grouping anagrams, two-sum pattern
  • Binary Search: Standard template, rotated array, search space reduction (không chỉ sorted array!)
  • Two Pointers: Same direction, opposite direction, giải được Container With Most Water, 3Sum
  • Sliding Window: Fixed size và variable size, giải được Minimum Window Substring
  • Tree Traversal: BFS (level order), DFS (pre/in/post), giải được LCA, path sum problems
  • Sorting Algorithms: Biết implement QuickSort và MergeSort, hiểu khi nào dùng cái nào

FAANG Mastery (Buổi 11-13)

  • Segment Tree: Build, range query, point update — giải được Range Sum Query Mutable
  • Binary Indexed Tree (Fenwick): Alternative cho Segment Tree, code ngắn hơn
  • Graph - BFS/DFS: Shortest path (unweighted), connected components, bipartite check
  • Graph - Dijkstra: Shortest path weighted graph, giải được Network Delay Time
  • Graph - Topological Sort: Kahn’s BFS và DFS approach, giải được Course Schedule I & II
  • Union Find: Path compression + rank, giải được Number of Islands II, Redundant Connection
  • Heap/Priority Queue: Custom comparator, giải được Merge K Lists, Find Median from Stream
  • Dynamic Programming - 1D: Fibonacci pattern, Climbing Stairs, House Robber, Coin Change
  • Dynamic Programming - 2D: Grid DP, Edit Distance, Longest Common Subsequence
  • DP - Knapsack: 0/1 Knapsack, Unbounded Knapsack, Partition Equal Subset Sum
  • Backtracking: Permutations, Combinations, Subsets, N-Queens, Sudoku Solver
  • Greedy: Activity Selection, Jump Game, Meeting Rooms, biết phân biệt với DP
  • Trie: Insert/Search/StartsWith, giải được Word Search II
  • Bit Manipulation: AND/OR/XOR tricks, single number, count bits, power of two
  • Math & Geometry: GCD/LCM, prime sieve, matrix rotation

Cách dùng checklist này

Với mỗi topic chưa tick: mở LeetCode, tìm 2-3 bài medium, giải trong 30 phút không xem solution. Nếu làm được → tick. Nếu không → quay lại học lại buổi đó.


6. Interview Readiness Score

Tự đánh giá từng topic từ 1-10, tính weighted average để có Overall FAANG Score.

Hướng dẫn chấm điểm

  • 1-3: Còn nhớ mang máng, cần xem lại từ đầu
  • 4-6: Hiểu concept, làm được medium với một ít struggle
  • 7-8: Tự tin làm medium trong 20-25 phút, biết cách approach hard
  • 9-10: Làm được hard trong 30-35 phút, giải thích được cho người khác
TopicTrọng sốScore (1-10)Weighted
Array / String / HashMap15%____
Binary Search8%____
Two Pointers / Sliding Window10%____
Tree (BFS/DFS)10%____
Graph (BFS/DFS/Topo)10%____
Dynamic Programming15%____
Heap / Priority Queue8%____
Backtracking7%____
Union Find5%____
Greedy5%____
Bit Manipulation4%____
Trie / Segment Tree3%____
OVERALL SCORE100%__

Ý nghĩa Overall Score:

ScoreÝ nghĩaHành động
≥ 8.0FAANG ReadyĐặt lịch phỏng vấn ngay
7.0 - 7.9Almost There2-3 tuần luyện thêm weak areas
6.0 - 6.9On Track1-2 tháng luyện thêm, focus vào DP + Graph
< 6.0Need More TimeReview lại fundamentals, làm thêm 50 easy/medium

7. Next Steps After This Vault

Roadmap 60 ngày sau vault

Mục tiêu: không bị lãng quên kiến thứcxây dựng pattern recognition thực sự qua problems mới.

Luyện tập có cấu trúc

Tuần 1-2: Ôn lại và củng cố

  • Làm lại tất cả problems trong vault từ đầu, không xem solution
  • Ghi ra các patterns và templates vào notebook riêng
  • Fix các topics có score < 7 trong readiness score

Tuần 3-8: 50 LeetCode Problems mới Phân bổ theo tần suất xuất hiện trong FAANG interviews:

  • 10 bài DP (mix 1D và 2D)
  • 8 bài Graph (mix BFS/DFS/Dijkstra)
  • 8 bài Tree (Binary Tree + BST)
  • 7 bài Array/String (Sliding Window + Two Pointers)
  • 5 bài Backtracking
  • 5 bài Heap/Priority Queue
  • 4 bài Binary Search
  • 3 bài Union Find / Trie

Quy tắc mỗi bài:

  1. Đặt timer 30 phút (Medium) hoặc 45 phút (Hard)
  2. Nếu không giải được sau 2/3 thời gian → ghi lại pattern đang bí
  3. Sau khi giải: đọc top discussions, học cách người khác tối ưu

Mock Interview Platforms

Recommended platforms

  • Pramp (pramp.com): Free peer-to-peer mock interviews, simulate real conditions
  • interviewing.io: Anonymous practice với engineers từ Google/Meta/Amazon
  • LeetCode Premium: Company-specific question lists (Google, Meta, Amazon, etc.)
  • NeetCode.io: Structured roadmap, video explanations tốt nhất hiện tại

System Design

Sau khi DSA solid, System Design là phần quyết định senior-level offers.

Vault tiếp theo: System-Design-Mastery

Topics cần cover:

  • Scalability fundamentals (horizontal/vertical scaling, load balancing)
  • Database design (SQL vs NoSQL, sharding, replication)
  • Caching strategies (Redis, CDN, cache invalidation)
  • Message queues (Kafka, RabbitMQ)
  • Design classic systems: URL Shortener, Twitter Feed, Uber, Netflix

Timeline Recommendation

Trình độ hiện tạiMục tiêuTimeline
Junior (< 2 năm)Intern/Junior FAANG4-6 tháng intensive
Mid-level (2-4 năm)L4/L5 Engineer2-3 tháng focused
Senior (4+ năm)L5/L6 Engineer6-8 tuần + System Design

Daily routine đề xuất:

  • Sáng (45 phút): 1 LeetCode medium mới — focus vào tốc độ và pattern recognition
  • Tối (30 phút): Review 2-3 problems đã làm hôm qua — spaced repetition
  • Cuối tuần (2 giờ): 1 mock interview session đầy đủ theo format buổi 12-13

8. Final Words from Your Sensei

Lời kết từ một FAANG Senior Engineer

“Mục tiêu không phải là ghi nhớ 500 LeetCode solutions. Mục tiêu là xây dựng pattern recognition mạnh đến mức bạn có thể phân tách bất kỳ bài toán nào bạn chưa gặp thành các patterns bạn ĐÃ gặp.

Khi tôi ngồi phỏng vấn, tôi không nghĩ ‘bài này tôi đã làm rồi’. Tôi nghĩ: ‘bài này hỏi về tối ưu trên một chuỗi con — sliding window. Có duplicate — cần HashMap. Minimize — co window khi hợp lệ. Xong, bắt đầu code.’

Đây là mindset bạn cần phát triển. Và nó không đến từ việc đọc solutions — nó đến từ hàng trăm giờ ngồi bí, vắt óc suy nghĩ, rồi đột nhiên thấy pattern.

Bạn đã đi qua toàn bộ vault này. Bạn đã đặt nền móng. Bây giờ nhiệm vụ duy nhất là lặp đi lặp lại cho đến khi nó trở thành bản năng.

Không có shortcut. Nhưng có con đường — và bạn đang đi đúng hướng.

Chúc mừng bạn đã hoàn thành DSA Mastery. Bây giờ, hãy ra ngoài và chứng minh nó.”

A Senior Engineer who was once exactly where you are now

Bạn đã hoàn thành DSA Mastery Vault!

Đây không phải là kết thúc — đây là bắt đầu của giai đoạn thực chiến. Pattern recognition bạn đã xây dựng sẽ theo bạn suốt sự nghiệp, không chỉ qua được phỏng vấn FAANG.

Hành động tiếp theo ngay bây giờ:

  1. Tính Interview Readiness Score của bạn ở Section 6
  2. Chọn 3 weak topics để tập trung trong 2 tuần tới
  3. Đặt lịch mock interview đầu tiên trên Pramp hoặc interviewing.io

Navigation — Vault Links