🔢 Algorithms✍️ Khoa📅 19/04/2026☕ 5 phút đọc

Algorithms: Interview Pattern Recognition Guide

1. Pattern Recognition Cheat Sheet

Khi thấy... → nghĩ đến...

Dấu hiệu trong đề Pattern
Subarray/substring liên tiếp, max/min/longest Sliding Window
Array đã sort, tìm cặp/bộ ba thỏa điều kiện Two Pointers
Array đã sort, tìm giá trị / vị trí Binary Search
"Minimum feasible X" hoặc "Maximum possible X" Binary Search on answer space
"Bao nhiêu cách", "tồn tại không", "tối ưu" Dynamic Programming
Subproblems overlap, bài con nhỏ hơn DP + Memoization
Dependency ordering, prerequisites Topological Sort
Connected components, same group? Union-Find
Shortest path, unweighted BFS
Shortest path, weighted non-negative Dijkstra
Tìm kết quả tức thời từ stream (running max, min, median) Heap / Monotonic Stack
Tìm "next greater element" Monotonic Stack
Prefix sum queries Prefix Sum Array
Range sum/min/max queries Segment Tree / Fenwick Tree

2. Complexity Cheat Sheet

Data Structure Operations:
  Array access:        O(1)
  Array search:        O(n)
  Hash map get/set:    O(1) average, O(n) worst
  Binary search tree:  O(log n) balanced, O(n) worst
  Heap push/pop:       O(log n)
  Heap peek:           O(1)

Sorting:
  QuickSort:    O(n log n) avg, O(n²) worst
  MergeSort:    O(n log n) always, O(n) space
  HeapSort:     O(n log n) always, O(1) space
  Counting/Radix: O(n) for small range

Graph:
  BFS/DFS:      O(V + E)
  Dijkstra:     O((V+E) log V)
  Bellman-Ford: O(VE)
  Floyd-Warshall: O(V³)
  Topological Sort: O(V + E)
  Union-Find:   O(α(n)) ≈ O(1)

3. Approach Framework cho interview

RRTED — 5 bước:

1. Repeat     — Nhắc lại bài, confirm constraints
2. Refine     — Hỏi edge cases, input size, negative numbers?
3. Think      — Nói ra brute force trước, rồi optimize
4. Execute    — Code với variable names rõ ràng
5. Debug      — Trace qua example nhỏ

Câu hỏi nên hỏi khi nhận đề

- Input size n? (ảnh hưởng O(n²) vs O(n log n))
- Có thể có negative numbers không?
- Array đã sorted chưa?
- Có duplicate không?
- Return value khi không tìm thấy? (-1, [], error?)
- Modify in-place hay return mới?

4. Nhận diện nhanh theo time complexity target

Nếu n ≤ 20: O(2ⁿ) chấp nhận được → backtracking, bitmask DP

Nếu n ≤ 500: O(n²) OK → DP 2D, brute force pairs

Nếu n ≤ 10⁵: O(n log n) cần → sorting, binary search, heap

Nếu n ≤ 10⁶: O(n) cần → sliding window, two pointers, hash map

Nếu n ≤ 10⁹: O(log n) cần → binary search, math


5. Common Patterns với Example

Monotonic Stack — Next Greater Element

nums = [2, 1, 2, 4, 3]
result = [4, 2, 4, -1, -1]

Stack duy trì các element chưa tìm được "next greater"
Khi gặp nums[i] > stack.top() → stack.top() tìm thấy answer
func nextGreaterElement(nums []int) []int {
    n := len(nums)
    result := make([]int, n)
    for i := range result { result[i] = -1 }
    stack := []int{} // lưu index

    for i := 0; i < n; i++ {
        for len(stack) > 0 && nums[i] > nums[stack[len(stack)-1]] {
            idx := stack[len(stack)-1]
            stack = stack[:len(stack)-1]
            result[idx] = nums[i]
        }
        stack = append(stack, i)
    }
    return result
}

Prefix Sum — Range Query

nums = [1, 2, 3, 4, 5]
prefix = [0, 1, 3, 6, 10, 15]
sum(l, r) = prefix[r+1] - prefix[l]  // O(1) query
func buildPrefix(nums []int) []int {
    prefix := make([]int, len(nums)+1)
    for i, v := range nums {
        prefix[i+1] = prefix[i] + v
    }
    return prefix
}

func rangeSum(prefix []int, l, r int) int {
    return prefix[r+1] - prefix[l]
}

Backtracking Template

func backtrack(path []int, choices []int, result *[][]int) {
    if isSolution(path) {
        *result = append(*result, append([]int{}, path...))
        return
    }
    for _, choice := range choices {
        if isValid(choice, path) {
            path = append(path, choice)         // choose
            backtrack(path, nextChoices, result) // explore
            path = path[:len(path)-1]           // unchoose
        }
    }
}

6. Các lỗi phổ biến nhất

Lỗi Tần suất Fix
Integer overflow khi tính mid Rất cao left + (right-left)/2
Off-by-one trong binary search Cao Dùng template cố định
Không xử lý empty/nil input Cao Check trước khi dùng
Modify slice đang iterate Trung bình Copy trước hoặc iterate index
Forget to mark visited (graph) Cao Luôn mark ngay khi thêm vào queue
Stack overflow trong DFS sâu Thấp Dùng iterative DFS
Quên base case trong DP Cao Viết rõ base case đầu tiên
Trả về reference thay vì copy Trung bình append([]int{}, path...)

7. Template "Big 5" — Học thuộc lòng

// 1. Binary Search (Lower Bound)
left, right := 0, n
for left < right {
    mid := left + (right-left)/2
    if condition(mid) { right = mid } else { left = mid + 1 }
}
return left

// 2. Sliding Window (Variable)
left := 0
for right := 0; right < n; right++ {
    add(arr[right])
    for invalid() { remove(arr[left]); left++ }
    updateAnswer(right - left + 1)
}

// 3. BFS
queue := []int{start}; visited[start] = true
for len(queue) > 0 {
    node := queue[0]; queue = queue[1:]
    for _, nb := range graph[node] {
        if !visited[nb] { visited[nb] = true; queue = append(queue, nb) }
    }
}

// 4. DFS (recursive)
func dfs(node int) {
    visited[node] = true
    for _, nb := range graph[node] { if !visited[nb] { dfs(nb) } }
}

// 5. Union-Find
func find(x int) int {
    if parent[x] != x { parent[x] = find(parent[x]) }
    return parent[x]
}
func union(x, y int) { parent[find(x)] = find(y) }

💡 Interview: Nói ra pattern trước khi code. "Tôi thấy bài này yêu cầu tìm subarray liên tiếp tối ưu → sliding window. Complexity sẽ là O(n)." Interviewer evaluate tư duy nhiều hơn code syntax.