Skip to content

Thuật toán & Cấu trúc dữ liệu 🧠

Thuật toán không chỉ là kiến thức để vượt qua vòng phỏng vấn — đó là nền tảng tư duy phân biệt một kỹ sư senior với người chỉ biết "code chạy được". Khi bạn chọn HashMap thay vì duyệt mảng lồng nhau, bạn đang áp dụng thuật toán. Khi bạn thiết kế Redis cache với chiến lược eviction phù hợp, bạn đang áp dụng thuật toán. Khi bạn tối ưu câu query từ 12 giây xuống 40ms, bạn đang áp dụng thuật toán.

Trong production, một quyết định sai về độ phức tạp có thể biến API response 50ms thành 5 phút khi dữ liệu tăng từ 1.000 lên 1.000.000 bản ghi. Đó là sự khác biệt giữa hệ thống scale được và hệ thống sập lúc 3 giờ sáng. Hiểu thuật toán giúp bạn nhìn ra vấn đề trước khi nó xảy ra — không phải sau khi khách hàng phàn nàn.

PENALGO xây dựng chương trình học thuật toán theo hướng engineering-first: mỗi khái niệm đều gắn với bài toán thực tế, mỗi thuật toán đều có phân tích production trade-off, và mỗi dòng code đều được giải thích tại sao nó tồn tại — không phải chỉ cách nó hoạt động.

Kiến trúc chương trình học

Chương trình được thiết kế theo lộ trình từ nền tảng đến chuyên sâu, mỗi phần xây dựng trên kiến thức của phần trước. Bạn không cần học tuần tự tất cả — nhưng nền tảng ở phần 1–3 là bắt buộc trước khi tiến vào các chủ đề nâng cao.

1. Nền tảng — Big O Notation

Ngôn ngữ chung của mọi kỹ sư phần mềm khi nói về hiệu năng. Trước khi phân tích bất kỳ thuật toán nào, bạn cần nắm vững cách đo lường Time Complexity và Space Complexity — từ O(1) đến O(n!), từ best-case đến amortized analysis. Đây là bước đầu tiên không thể bỏ qua.

Bắt đầu với Big O Notation

2. Sắp xếp (Sorting)

Sorting là "Hello World" của thuật toán — đơn giản để hiểu nhưng sâu để thành thạo. Từ Bubble Sort giúp bạn hiểu tư duy so sánh-hoán đổi, đến Quick Sort với chiến lược pivot quyết định hiệu năng thực tế, đến Timsort — thuật toán mà Python và Java thực sự dùng trong production. Mỗi thuật toán sắp xếp dạy bạn một bài học thiết kế khác nhau.

Khám phá Sorting Algorithms

3. Tìm kiếm (Searching)

Binary Search không chỉ là "chia đôi mảng đã sắp xếp" — nó là một paradigm tư duy áp dụng được cho mọi bài toán có tính chất đơn điệu. Từ tìm kiếm tuyến tính cơ bản đến các biến thể binary search trong bài toán tối ưu hóa, phần này xây dựng trực giác giúp bạn nhận ra khi nào có thể giảm O(n) xuống O(log n).

Khám phá Searching Algorithms

4. Đồ thị (Graph)

Mạng xã hội, hệ thống routing, dependency resolution, task scheduling — tất cả đều là bài toán đồ thị. Phần này đưa bạn từ BFS/DFS cơ bản, qua các thuật toán đường đi ngắn nhất (Dijkstra, Bellman-Ford, Floyd-Warshall), đến Minimum Spanning Tree, Topological Sort, và Max Flow. Đồ thị là cầu nối giữa thuật toán lý thuyết và kiến trúc hệ thống thực tế.

Khám phá Graph Algorithms

5. Quy hoạch động (Dynamic Programming)

DP là chủ đề khiến nhiều kỹ sư sợ nhất — và cũng là chủ đề mang lại lợi thế lớn nhất khi thành thạo. Bản chất của DP là nhận ra bài toán có cấu trúc con tối ưu và các bài toán con chồng lấn, từ đó chuyển giải pháp brute-force O(2ⁿ) thành O(n²) hoặc tốt hơn. Từ Climbing Stairs đến Knapsack, từ Memoization đến Tabulation — mỗi bài kinh điển dạy bạn một pattern có thể tái sử dụng.

Khám phá Dynamic Programming

6. Tham lam & Quay lui (Greedy & Backtracking)

Hai paradigm đối lập nhưng bổ sung cho nhau. Greedy đưa ra quyết định tối ưu cục bộ tại mỗi bước và hy vọng đạt kết quả tối ưu toàn cục — từ Huffman Coding (nền tảng nén dữ liệu) đến Activity Selection. Backtracking thì khám phá mọi khả năng và quay lui khi gặp ngõ cụt — từ N-Queens đến Sudoku Solver. Biết khi nào dùng greedy và khi nào cần backtracking là kỹ năng thiết kế thuật toán cốt lõi.

Khám phá Greedy & Backtracking

7. Cấu trúc dữ liệu nâng cao

Vượt ra ngoài Array và LinkedList, phần này khám phá các cấu trúc dữ liệu cho phép thao tác O(log n) trên dữ liệu phức tạp. Heap cho priority queue và CPU scheduling, Segment Tree cho range query trong phân tích dữ liệu real-time, Fenwick Tree cho prefix sum hiệu quả, B-Tree cho database indexing, và Consistent Hashing cho distributed systems.

Khám phá Advanced Data Structures

8. Thao tác bit (Bit Manipulation)

Lập trình ở mức thấp nhất — nơi mỗi bit đều có ý nghĩa. XOR tricks giúp giải bài toán trong O(1) space, Bitmasking biến bài toán tập hợp con thành phép toán trên số nguyên, và Fast Exponentiation là nền tảng của mật mã RSA. Đây là vũ khí bí mật cho các bài toán yêu cầu hiệu năng tối đa và bộ nhớ tối thiểu.

Khám phá Bit Manipulation

9. Xử lý chuỗi (String Processing)

Từ pattern matching cơ bản đến các thuật toán chuyên biệt cho text search engine. KMP và Rabin-Karp cho single-pattern matching, Aho-Corasick cho multi-pattern matching (nền tảng của antivirus scanner và content filter), và Trie cho autocomplete và spell checker. Chuỗi là kiểu dữ liệu phổ biến nhất trong mọi ứng dụng — xử lý hiệu quả là kỹ năng không thể thiếu.

Khám phá String Processing

10. Quy mô & Xấp xỉ (Scale & Approximation)

Khi dữ liệu quá lớn để xử lý chính xác, bạn cần các thuật toán xấp xỉ thông minh. Bloom Filter trả lời "phần tử này có trong tập hợp không?" với O(1) và sai số kiểm soát được, HyperLogLog đếm số phần tử phân biệt trong hàng tỷ bản ghi chỉ với 12KB bộ nhớ, và Quadtree phân vùng không gian cho bài toán geo-spatial. Đây là thuật toán mà các hệ thống Big Data thực sự sử dụng.

Khám phá Scale & Approximation

11. Mẫu tối ưu hóa (Optimization Patterns)

Những pattern thuật toán xuất hiện lặp đi lặp lại trong production code. Sliding Window cho streaming data và rate limiting, Floyd's Cycle Detection cho linked list và trạng thái lặp, Token Bucket cho API rate limiting, và Compare-And-Swap cho lock-free concurrency. Đây không phải thuật toán hàn lâm — đây là công cụ bạn sẽ dùng hàng ngày trong hệ thống thực.

Khám phá Optimization Patterns

12. Hình học tính toán (Computational Geometry)

Thuật toán trên không gian hình học — từ Convex Hull cho collision detection trong game engine đến các bài toán điểm-đoạn-đa giác trong GIS và bản đồ. Tuy chuyên biệt hơn các phần khác, hình học tính toán là nền tảng cho computer graphics, robotics, và hệ thống định vị.

Khám phá Computational Geometry

Bắt đầu từ đâu?

Nếu bạn mới bắt đầu học thuật toán — hãy đi theo thứ tự: Big O → Sorting → Searching → Dynamic Programming. Bốn phần này xây dựng nền tảng tư duy thuật toán vững chắc nhất. Đừng vội nhảy vào Graph hay Bit Manipulation khi chưa thoải mái với việc phân tích độ phức tạp.

Nếu bạn đã có kinh nghiệm và muốn nâng cao — Graph, Dynamic Programming, và Advanced Data Structures là ba trụ cột mang lại khác biệt lớn nhất trong phỏng vấn lẫn production. Kết hợp với Optimization Patterns để áp dụng ngay vào hệ thống thực tế.

Nếu bạn đang chuẩn bị phỏng vấn Big Tech — tập trung vào Dynamic Programming, Graph, và Backtracking. Bổ sung Bit Manipulation và String Processing cho các bài toán niche. Quan trọng nhất: luyện phân tích trade-off giữa time và space complexity cho mỗi bài toán.

Nếu bạn là system engineer quan tâm đến infrastructure — Scale & Approximation, Optimization Patterns, và Advanced Data Structures sẽ có giá trị trực tiếp nhất. Bloom Filter, Consistent Hashing, và Token Bucket là những thuật toán bạn sẽ gặp hàng ngày trong distributed systems.

Khuyến nghị

Dù ở trình độ nào, hãy bắt đầu với Big O Notation — đó là ngôn ngữ chung giúp bạn đánh giá mọi thuật toán và cấu trúc dữ liệu trong chương trình này.

Liên kết nhanh đến từng phần

🧠 Quiz

Câu 1: Trong phân tích thuật toán, Big O biểu diễn điều gì?

  • [ ] A) Thời gian chạy chính xác tính bằng mili-giây
  • [x] B) Tốc độ tăng trưởng của thời gian/bộ nhớ khi input tăng
  • [ ] C) Số dòng code của thuật toán
  • [ ] D) Dung lượng RAM cần thiết

💡 Giải thích: Big O mô tả tốc độ tăng trưởng (growth rate) — không phải thời gian chạy cụ thể. O(n log n) nghĩa là khi input tăng gấp đôi, thời gian tăng hơn gấp đôi một chút. Đây là ngôn ngữ chung để so sánh hiệu suất thuật toán.

Câu 2: Khi nào nên ưu tiên thuật toán O(n²) thay vì O(n log n)?

  • [ ] A) Không bao giờ, O(n log n) luôn tốt hơn
  • [ ] B) Khi dữ liệu đã được sắp xếp
  • [x] C) Khi n rất nhỏ (< 50) và thuật toán O(n²) đơn giản hơn, constant factor nhỏ hơn
  • [ ] D) Khi cần tiết kiệm bộ nhớ

💡 Giải thích: Big O giấu constant factor. Với n nhỏ, thuật toán đơn giản O(n²) như Insertion Sort có thể nhanh hơn Merge Sort O(n log n) nhờ ít overhead. Timsort (Python/Java) dùng Insertion Sort cho đoạn nhỏ < 64 phần tử vì lý do này.

Câu 3: Cấu trúc dữ liệu nào cho phép tìm kiếm, chèn, xóa đều O(log n) trong worst case?

  • [ ] A) Hash Table
  • [ ] B) Array đã sắp xếp
  • [x] C) Balanced Binary Search Tree (AVL/Red-Black Tree)
  • [ ] D) Linked List

💡 Giải thích: Balanced BST đảm bảo O(log n) cho cả ba thao tác nhờ tự cân bằng. Hash Table trung bình O(1) nhưng worst case O(n). Array sắp xếp tìm nhanh O(log n) nhưng chèn/xóa O(n). Linked List tìm O(n).