Nền tảng luyện tập & Chuẩn bị phỏng vấnPlatforms to Practice & Interview Preparation
Mục lục
- Tổng quan
- Kiến thức nền tảng
- Các nền tảng, và mỗi cái thực sự dùng để làm gì
- Vòng lặp luyện tập
- Timebox, và vì sao việc vật lộn cũng có giới hạn
- Khái niệm chính
- Thứ tự đề xuất đi qua section này
- Danh sách pattern
- Giao tiếp trong buổi phỏng vấn kỹ thuật
- Những kiểu thất bại thường gặp
- Tuần cuối trước buổi phỏng vấn
- Best Practices
- Tài liệu tham khảo
Table of contents
- Overview
- Fundamentals
- The platforms, and what each is actually for
- The practice loop
- Timeboxing, and why struggling has a limit
- Key Concepts
- A suggested order through this section
- The pattern list
- Communicating in a technical interview
- Common failure modes
- The last week before an interview
- Best Practices
- References
Thuộc bộ kiến thức Data Structures & Algorithms Roadmap.
Tổng quan
Toàn bộ hai mươi ba topic trước là kiến thức bạn có thể thu nạp bằng cách đọc. Topic này thì không. Data structures và algorithms là một kỹ năng, mà kỹ năng thì chỉ có được bằng cách làm dở, nhận phản hồi, rồi làm lại — chứ không phải bằng cách đọc về những người làm giỏi.
Sai lầm gần như ai cũng mắc là coi việc luyện tập như một bài toán số lượng: “tôi sẽ làm 300 bài”. Số lượng mà không có cấu trúc sẽ tạo ra những người đã nhìn qua 300 bài và giải được khoảng 40 bài, vì 260 bài còn lại họ “giải” bằng cách đọc editorial sau hai mươi phút rồi thấy như mình đã hiểu. Hiểu một lời giải và tạo ra được một lời giải là hai kỹ năng khác nhau, cách nhau chín tháng, và chỉ một trong hai là thứ mà phỏng vấn hay deadline thực tế kiểm tra.
Thứ có tác dụng thì nhỏ hơn và kém hào nhoáng hơn: học một pattern, luyện nó trên năm tới mười bài cho tới khi pattern thành phản xạ, rồi đi tiếp và quay lại sau một tuần. Mười hai pattern, mỗi cái mười bài, là 120 bài — ít hơn số bài phần lớn mọi người cày — nhưng cho kết quả tốt hơn hẳn, vì sau đó bạn nhận ra hình dạng của bài toán thay vì cố nhớ xem mình đã gặp đúng bài này chưa.
Bài này nói về luyện ở đâu, luyện thế nào, theo thứ tự nào, và cách biến việc luyện tập đó thành một buổi phỏng vấn đạt. Phần nội dung kỹ thuật mà nó dựa vào nằm ở các topic pattern: quy hoạch động, hai con trỏ & cửa sổ trượt, và độ phức tạp thuật toán — cái cuối cùng là thứ bạn sẽ bị hỏi trong mọi buổi phỏng vấn, bất kể đề bài là gì.
Kiến thức nền tảng
Các nền tảng, và mỗi cái thực sự dùng để làm gì
Các platform không thay thế được cho nhau. Mỗi cái tối ưu cho một mục tiêu khác nhau, và chọn sai cái cho mục tiêu của bạn sẽ phí hàng tháng trời.
| Platform | Phù hợp nhất cho | Định dạng | Ghi chú |
|---|---|---|---|
| LeetCode | Phỏng vấn software engineering | Bài lẻ, gắn tag theo công ty và chủ đề | Lựa chọn mặc định, và đúng là như vậy. Chất lượng editorial không đều; tab discussion thường tốt hơn |
| HackerRank | Track có cấu trúc, bài test sàng lọc | Bài tập + chứng chỉ + contest | Nhiều công ty dùng nó cho vòng screening tự động, nên đáng để quen với kiểu I/O của nó |
| Codeforces | Competitive programming, tốc độ tư duy | Contest có rating, Div. 1–4 | Chất lượng ra đề tốt nhất ngành. Hơi quá đà cho phỏng vấn, nhưng tuyệt vời cho năng lực thô |
| AtCoder | Đường tăng độ khó sạch sẽ, chấm tốt | Contest Beginner/Regular hàng tuần | Contest ABC là cái thang được biên soạn tốt nhất để đi từ “biết viết vòng lặp” tới “biết tư duy”. Editorial có tiếng Anh |
| CodeChef | Contest dài, lộ trình học có cấu trúc | Contest + ladder luyện tập | Định dạng Long Challenge thưởng cho việc tra cứu và kiên trì hơn là tốc độ |
| Edabit | Người mới hoàn toàn, làm quen cú pháp | Bài tập rất nhỏ, kiểu game hóa | Không phải luyện thuật toán. Đây là công cụ đúng cho giai đoạn “còn vật lộn với cú pháp” trong nền tảng lập trình |
| Exercism | Thành thạo ngôn ngữ + review code bởi người thật | Bài tập theo từng ngôn ngữ, có mentor phản hồi | Phần mentorship mới là điểm chính. Hợp với “tôi muốn viết Go idiomatic”, không hợp với “tôi muốn qua phỏng vấn” |
| Codewars | Xây thói quen, làm đều mỗi ngày | Kata xếp hạng theo kyu, có lời giải cộng đồng | So lời giải của mình với các bản được vote cao sau khi giải xong thực sự rất bổ ích |
| Project Euler | Giải toán mang tính toán học | Bài đậm chất number theory, không giới hạn thời gian | Không phải luyện phỏng vấn. Tuyệt vời cho nửa “toán” của tư duy thuật toán |
| InterviewBit | Lộ trình luyện phỏng vấn có dẫn dắt | Chương trình sắp theo chủ đề, có giới hạn thời gian | Giá trị nằm ở thứ tự chương trình — đã có người quyết định hộ bạn học gì lúc nào |
| CSES Problem Set | Phủ thuật toán một cách hệ thống | 300 bài chia theo chủ đề, không có editorial | Danh sách có cấu trúc miễn phí tốt nhất hiện có. Giải hết mục “Dynamic Programming” và “Graph Algorithms” là bạn thực sự biết DP và graph |
| Advent of Code | Giải đố vui, thiên về thực dụng | 25 ngày tháng Mười Hai, mỗi ngày hai phần | Là câu đố chứ không phải câu hỏi phỏng vấn — parsing, mô phỏng, lưới. Rất hợp để giữ thói quen sống sót qua tháng Mười Hai |
Nếu chỉ muốn một gợi ý duy nhất: LeetCode để luyện phỏng vấn, CSES để xây chiều sâu thuật toán thật, Codeforces hoặc AtCoder nếu bạn thấy mình thích thứ này và muốn giỏi thật sự. Mọi thứ khác là tùy chọn.
Một ghi chú về các danh sách được biên soạn kiểu “Blind 75” / “NeetCode 150” / “Grind 75”: chúng rất tốt và bạn nên dùng một cái. Giá trị của chúng không nằm ở đề bài (chỉ là các bài LeetCode) mà ở sự biên soạn — có người đủ trình đã loại bỏ phần trùng lặp và phần nhiễu, nên 75 bài phủ đúng không gian pattern mà 400 bài ngẫu nhiên mới phủ được. Hãy chọn một danh sách và làm hết; đừng đi sưu tầm danh sách.
Vòng lặp luyện tập
Đây là vòng lặp thực sự xây dựng kỹ năng, áp dụng cho mỗi bài:
- Đọc đề hai lần. Phát biểu lại bằng lời của bạn. Ghi lại ràng buộc input — đó là gợi ý mạnh nhất về độ phức tạp mong muốn (xem phần nhận diện trong quy hoạch động).
- Bấm giờ. 25 phút cho easy/medium, 40 phút cho hard. Trong 5–10 phút đầu, chỉ nghĩ, không viết code.
- Giải được, hoặc thất bại một cách sạch sẽ. “Thất bại sạch sẽ” nghĩa là: khi hết giờ, hãy viết ra bạn đã thử gì và tắc ở đâu, trong một câu, trước khi mở bất cứ thứ gì.
- Đọc editorial — cho tử tế. Không phải “lướt tới lúc thấy thông”. Đọc tới khi bạn giải thích được insight then chốt mà không cần nhìn. Insight then chốt thường chỉ là một câu (“sort theo thời điểm kết thúc”, “state là prefix của mỗi chuỗi”, “dịch đường thấp hơn”). Viết câu đó ra.
- Đóng hết mọi thứ và tự viết lại từ đầu. Bước này không phải tùy chọn, và nó chính là bước ai cũng bỏ qua. Đọc lời giải tạo ra ảo giác thành thạo; tái tạo lại nó từ một câu insight mới tạo ra sự thành thạo.
- Ghi log. Tên bài, pattern, câu insight, và kết quả ra sao. Một spreadsheet hay một file text thuần là đủ.
- Một tuần sau, giải lại từ đầu, chỉ dựa vào dòng log. Nếu không giải được thì bạn chưa học được — bạn chỉ nhận ra nó thôi. Đọc lại và xếp lịch lại.
Bước 7 chính là spaced repetition, và đó là thói quen có đòn bẩy cao nhất trong cả bài này. Lịch chạy tốt: 1 ngày, 1 tuần, 1 tháng. Ba lần lặp với khoảng cách giãn dần biến một lời giải từ “tôi từng thấy cái này” thành thứ bạn nhớ lại được dưới áp lực. Anki hợp với việc này nếu bạn đã dùng nó; mặt trước của card là tên bài kèm ràng buộc, mặt sau là câu insight của bạn.
Timebox, và vì sao việc vật lộn cũng có giới hạn
Đồng hồ 25 phút là sự thỏa hiệp giữa hai kiểu thất bại. Bỏ cuộc sau năm phút thì bạn không bao giờ phát triển được khả năng ngồi lì với một bài chưa giải được — vốn chính là khả năng mà phỏng vấn kiểm tra. Nhất định không mở editorial trong ba tiếng thì bạn đốt cả buổi tối để học một thứ thay vì năm thứ.
Quy tắc đúng: hãy vật lộn một cách hiệu quả cho tới khi bạn ngừng sinh ra ý tưởng mới, rồi dừng. Nếu bạn đã mất mười phút quay vòng qua đúng ba hướng đã thất bại, bạn không đang học, bạn đang ngâm. Hãy tra, rút lấy insight, rồi viết lại — chỗ viết lại mới là chỗ học được.
Một bước trung gian hữu ích trước khi đọc editorial đầy đủ: chỉ đọc tag, hoặc chỉ đọc đoạn đầu tiên của editorial. “Đây là bài sliding window” thường đã là toàn bộ chìa khóa bạn cần, và bạn vẫn giữ được phần lớn giá trị của việc tự giải.
Khái niệm chính
Thứ tự đề xuất đi qua section này
Thứ tự quan trọng hơn phần lớn mọi người nghĩ, vì khoảng một nửa số topic ở đây là điều kiện tiên quyết cho nửa còn lại. Đâm đầu vào graph trước khi thoải mái với queue và đệ quy nghĩa là học ba thứ cùng lúc và chẳng thứ nào ra hồn.
Giai đoạn 1 — Nền tảng (không bỏ qua, cũng đừng nấn ná).
- Nền tảng lập trình & pseudocode — chỉ khi bạn chưa thành thạo ngôn ngữ của mình. Edabit hoặc Exercism là công cụ đúng ở đây.
- Cấu trúc dữ liệu là gì và độ phức tạp thuật toán. Complexity là từ vựng của mọi cuộc thảo luận về sau và của mọi buổi phỏng vấn. Hãy học nó cho tử tế ngay bây giờ.
- Array, linked list, stack & queue, hash table. Khoảng 80% bài phỏng vấn được giải bằng một array, một hash map, hoặc cả hai.
Giai đoạn 2 — Bộ công cụ thuật toán cốt lõi.
- Sắp xếp và tìm kiếm. Binary search xứng đáng nhiều thời gian hơn mức nó thường được dành — đặc biệt là “binary search trên đáp án”, kỹ thuật hiệu suất cao mà ít người luyện nhất trong danh sách này.
- Hai con trỏ & cửa sổ trượt. Đúng vậy, không theo thứ tự số — nó chỉ phụ thuộc vào array, xuất hiện liên tục, và là chiến thắng tự tin đến nhanh nhất. Hãy làm nó ở đây.
- Đệ quy & chia để trị. Mọi thứ sau đây đều giả định bạn đã thoải mái với đệ quy.
Giai đoạn 3 — Cây và đồ thị.
- Cấu trúc dữ liệu cây — traversal, BST. Rồi tới heap & priority queue, thứ mở khóa nhóm bài top-k và merge-k.
- Cấu trúc dữ liệu đồ thị — BFS, DFS, connected component, topological sort. Đây là nơi cư trú của một phần lớn các bài “medium/hard”.
- Đường đi ngắn nhất, cây khung nhỏ nhất, union-find.
Giai đoạn 4 — Các kỹ thuật giải quyết bài toán.
- Brute force, greedy & thuật toán ngẫu nhiên, rồi backtracking.
- Quy hoạch động. Để cuối cùng, một cách có chủ đích: nó khó học nhất và xây trực tiếp trên đệ quy cùng memoization. Lao vào DP đầu tiên là lý do phổ biến nhất khiến người ta kết luận rằng mình “dốt thuật toán”.
Giai đoạn 5 — Chiều sâu, nếu bạn muốn.
- Cây cân bằng & cây nhiều nhánh, cấu trúc cây nâng cao (trie, segment tree, Fenwick), indexing. Trie xuất hiện trong phỏng vấn khá thường xuyên; segment tree thì gần như không bao giờ, nhưng lại rất phổ biến trong competitive programming.
Riêng cho việc luyện phỏng vấn, Giai đoạn 1–4 chính là chương trình học. Giai đoạn 5 dành cho chiều sâu và cho competitive programming.
Danh sách pattern
Lý do nên tổ chức việc luyện tập theo pattern thay vì theo chủ đề là vì pattern mới là thứ bạn thực sự nhận ra khi bị áp lực thời gian. Đây là danh sách đáng thuộc nằm lòng — mười hai tới mười lăm pattern phủ áp đảo phần lớn các bài phỏng vấn:
| Pattern | Dấu hiệu kích hoạt | Được trình bày ở |
|---|---|---|
| Hashing để tra cứu O(1) | “Đã gặp cái này chưa”, phần bù, đếm | ./07-hash-tables.md |
| Two pointers (ngược chiều) | Mảng đã sorted, tìm cặp, palindrome | ./23-two-pointers-and-sliding-window.md |
| Sliding window | Subarray/chuỗi con liên tiếp kèm ràng buộc | ./23-two-pointers-and-sliding-window.md |
| Fast & slow pointers | Cycle trong linked list, phần tử giữa, thứ n từ cuối | ./23-two-pointers-and-sliding-window.md |
| Prefix sums | Tổng hợp trên đoạn, tổng subarray có số âm | ./23-two-pointers-and-sliding-window.md |
| Binary search (kể cả trên đáp án) | Input đã sorted, hoặc một predicate đơn điệu trên một khoảng | ./09-search-algorithms.md |
| Merge intervals / sweep line | Các đoạn chồng lấn, lập lịch, đỉnh đồng thời | ./23-two-pointers-and-sliding-window.md |
| Heap / top-k / two heaps | ”k lớn nhất”, “trung vị của một stream”, gộp k list | ./12-heaps-and-priority-queues.md |
| Monotonic stack | ”Phần tử lớn kế tiếp”, histogram, span | ./06-stacks-and-queues.md |
| Tree DFS / BFS | Mọi thứ trên cây; duyệt theo mức; tổng đường đi | ./10-tree-data-structures.md |
| Duyệt đồ thị + topological sort | Lưới, đảo, phụ thuộc, phát hiện cycle | ./13-graph-data-structures.md |
| Union-find | Tính liên thông, gom nhóm, Kruskal | ./16-disjoint-set-union-find.md |
| Backtracking | ”Sinh ra tất cả”, tập con, hoán vị, sudoku | ./21-backtracking.md |
| Dynamic programming | Đếm/max/min trên các lựa chọn, greedy sai có chứng minh | ./22-dynamic-programming.md |
| Greedy + lập luận trao đổi | Lựa chọn cục bộ tối ưu có chứng minh (nhớ chứng minh!) | ./20-brute-force-greedy-and-randomised-algorithms.md |
Hãy luyện từng dòng một. Năm tới mười bài trên cùng một pattern, làm liên tiếp, dạy bạn hình dạng của pattern đó — các biến thể, các edge case, và thời điểm nó thôi áp dụng được. Mười bài ngẫu nhiên rải trên mười pattern chỉ dạy bạn mười sự kiện rời rạc.
Giao tiếp trong buổi phỏng vấn kỹ thuật
Phỏng vấn không phải bài kiểm tra giải toán. Nó là một mô phỏng việc làm việc cùng bạn, và code là sản phẩm phụ chứ không phải trọng tâm. Ứng viên giải bài đúng nhưng im lặng thường bị chấm thấp hơn ứng viên giải bài có tương tác và cần một gợi ý. Quy trình dưới đây là thứ người phỏng vấn được huấn luyện để tìm kiếm.
1. Làm rõ trước khi viết code (2–3 phút). Hỏi về kích thước và khoảng giá trị của input, input rỗng và một phần tử, giá trị trùng lặp, số âm, input đã sorted chưa, có được sửa tại chỗ không, trả về gì khi không có đáp án, và input có vừa bộ nhớ không. Phát biểu lại đề bằng lời của bạn và xác nhận.
Đây không phải nghi thức. Đề phỏng vấn cố tình thiếu thông tin, và hỏi chính là cách bạn phát hiện ra rằng mảng đã sorted (mở khóa two pointers), rằng giá trị nằm trong 1..n (mở khóa cyclic sort), hoặc rằng n ≤ 20 (mở khóa bitmask DP). Các ràng buộc là một nửa lời giải.
2. Chạy tay một ví dụ. Lấy một input nhỏ và tự tạo ra output đúng bằng tay. Việc này bắt lỗi hiểu sai ngay lập tức và thường hé lộ cấu trúc của lời giải. Nó cũng cho bạn một test case để dùng về sau.
3. Nêu brute force kèm độ phức tạp, rồi nói rằng bạn có thể làm tốt hơn. Kiểu như: “Cách hiển nhiên là kiểm tra mọi cặp, tốn O(n²) thời gian và O(1) bộ nhớ. Để em xem có làm tốt hơn được không.” Đây là nước đi trội tuyệt đối. Nó cho thấy bạn hiểu đề, đặt sẵn một baseline chạy được lên bàn phòng khi hết giờ, và xác lập mốc độ phức tạp mà bạn đang muốn vượt qua. Đừng bao giờ ngồi im săn lời giải tối ưu — một brute force được nói ra có giá hơn một ý tưởng hay hơn nhưng không nói ra.
4. Nghĩ thành tiếng trong lúc tối ưu. Nói ra bạn đang cân nhắc gì và vì sao bạn loại nó: “Em có thể sort, cho O(n log n), nhưng đề nói chỉ số gốc có ý nghĩa nên em sẽ phải giữ lại chúng — để em thử hash map xem sao.” Những hướng bị loại kèm lý do là bằng chứng của khả năng phán đoán, vốn chính xác là thứ đang được đánh giá. Sự im lặng thì không đọc được và bị chấm là đang bí.
5. Chốt hướng tiếp cận trước khi viết code. “Vậy: hash map từ giá trị sang chỉ số, một lượt duyệt, O(n) thời gian và O(n) bộ nhớ. Em code luôn nhé?” Nếu người phỏng vấn định lái bạn sang hướng khác thì đây là lúc họ làm — và nó cứu bạn khỏi mười lăm phút viết nhầm thứ.
6. Viết code sạch và tường thuật. Tên biến thật, không phải a/b/tmp. Xử lý những edge case bạn đã nhận diện ở bước 1. Nói ra bạn đang làm gì trong lúc làm, nhưng đừng tường thuật cú pháp — hãy tường thuật ý định (“giờ em co cửa sổ lại cho tới khi nó hợp lệ trở lại”).
7. Tự test trước khi bị yêu cầu. Chạy tay code của chính bạn với ví dụ nhỏ ở bước 2, nói thành tiếng, từng dòng, theo dõi các biến. Rồi chạy các edge case: rỗng, một phần tử, toàn phần tử giống nhau, target ở biên. Tự tìm ra bug của mình là tín hiệu tích cực — nó cho thấy đúng thói quen tự kiểm tra mà bạn sẽ áp dụng khi đi làm. Bị người khác chỉ ra bug là tín hiệu trung tính tới tiêu cực.
8. Nêu độ phức tạp cuối cùng và các đánh đổi. “O(n) thời gian, O(n) bộ nhớ. Nếu bộ nhớ eo hẹp thì em sẽ sort trước rồi dùng two pointers — O(n log n) thời gian nhưng chỉ O(1) bộ nhớ phụ.” Gọi tên phương án bạn đã không chọn, kèm lý do, là tín hiệu cấp senior và chỉ tốn mười lăm giây.
Những kiểu thất bại thường gặp
Đây là những cách mà người đã chuẩn bị vẫn trượt, xếp đại khái theo tần suất.
- Cày số lượng mà không theo pattern. 400 bài, mỗi bài giải một lần, không đọng lại bài nào. Cách sửa là danh sách pattern cộng spaced repetition.
- Đọc lời giải rồi đi tiếp. Đây là lỗi lớn nhất. Nhận ra không phải là nhớ lại. Nếu bạn chưa đóng tab và viết lại thì bạn chưa học được nó.
- Không bao giờ luyện nói thành tiếng. Vừa nói vừa code là một kỹ năng vận động riêng biệt và nó thực sự khó ở lần đầu. Nếu lần đầu tiên bạn thử là trong buổi phỏng vấn thật, kết quả sẽ tệ. Hãy tập tường thuật kể cả khi ngồi một mình — phỏng vấn thử với bạn bè, hoặc Pramp/interviewing.io, thì tốt hơn.
- Nhảy thẳng vào code. Không làm rõ, không ví dụ, không nêu hướng. Kết cục là bạn giải một bài mà người phỏng vấn không hỏi, và phát hiện ra điều đó ở phút thứ hai mươi.
- Đuổi theo lời giải tối ưu rồi không giao được gì. Một bản
O(n²)chạy được kèm giải thích vềO(n)mà bạn không kịp viết vẫn hơn một bảnO(n)dang dở. - Bỏ qua edge case. Input rỗng, một phần tử, toàn phần tử trùng, tràn số nguyên (ở các ngôn ngữ không phải Python), target ở chỉ số 0 hoặc
n−1. Người phỏng vấn có sẵn một checklist trong đầu và họ tích từng ô. - Không nói được độ phức tạp của chính mình. Nếu bạn không nói được vì sao vòng lặp của bạn là
O(n log n)thì mọi thứ khác bạn nói đều không tính. Hãy đọc lại độ phức tạp thuật toán. - Chỉ luyện những bài khó nhất. Bài hard có tỷ lệ học-được-trên-giờ rất kém. Bài medium mới là phân phối thật của phỏng vấn và là tín hiệu luyện tập tốt nhất.
- Luyện trong IDE có autocomplete. Rồi đến ngày thi lại nhận một trình soạn thảo text thuần hoặc một cái bảng trắng. Hãy luyện trong môi trường bạn sẽ bị kiểm tra, ít nhất là một phần thời gian.
- Bỏ bê các vòng không phải thuật toán. Với phần lớn vị trí senior, vòng system design và vòng behavioural có trọng số ngang hoặc hơn vòng thuật toán. Hãy phân bổ thời gian chuẩn bị tương ứng.
- Burn out ba tuần trước buổi phỏng vấn. Hai giờ tập trung mỗi ngày trong tám tuần thắng tám giờ mỗi ngày trong hai tuần, thắng cách biệt, vì khả năng ghi nhớ là hàm của việc giãn cách chứ không phải của tổng số giờ.
Tuần cuối trước buổi phỏng vấn
Tuần cuối không phải để học kiến thức mới. Thứ gì bạn chưa học được tới giờ thì trong bảy ngày cũng sẽ không đáng tin, và việc nhồi nhét chiếm chỗ của phần luyện truy hồi vốn mới thực sự giúp ích.
Còn 7 ngày. Dừng mọi chủ đề mới. Giải lại 20–30 bài bạn đã làm, mỗi pattern một hai bài, dựa vào log của bạn. Tốc độ quan trọng ở đây — bạn đang xây lại khả năng nhớ lại, không phải xây lại sự hiểu.
Còn 5 ngày. Làm hai buổi phỏng vấn thử đầy đủ với người thật, nói thành tiếng, bấm giờ, trên một trình soạn thảo chia sẻ không có autocomplete. Đây là hoạt động giá trị nhất của cả tuần và cũng là thứ người ta né vì nó khó chịu. Chính sự khó chịu đó mới là điểm.
Còn 3 ngày. Rà lại log và liệt kê những pattern bạn vẫn còn ngập ngừng. Làm ba bốn bài cho mỗi cái. Đọc lại câu insight cho mọi thứ còn lại.
Còn 2 ngày. Tìm hiểu về công ty: vòng nào là vòng gì, kéo dài bao lâu, được dùng ngôn ngữ nào, làm trên doc chia sẻ hay IDE thật. Chuẩn bị câu hỏi của bạn dành cho họ, và hai ba câu chuyện dự án cho phần behavioural.
Còn 1 ngày. Một hai bài easy để giữ nhiệt — không bài khó, không bài mới. Đọc lại ghi chú ba mươi phút, không phải ba tiếng. Rồi dừng, và ngủ cho tử tế. Giấc ngủ giúp hiệu suất giải bài ngày hôm sau nhiều hơn bất kỳ lượng ôn tập phút chót nào, và mức chênh không hề nhỏ.
Đến ngày. Khởi động bằng một bài easy trước đó một tiếng, để dòng code đầu tiên bạn viết không phải là trong buổi phỏng vấn. Kiểm tra thiết bị: camera, micro, trình soạn thảo, một cốc nước, và giấy bút để vẽ. Chuẩn bị sẵn các câu hỏi làm rõ dưới dạng checklist — dưới áp lực bạn sẽ quên hỏi, mà hỏi thì đáng giá hơn bất cứ dòng code đơn lẻ nào bạn sắp viết.
Best Practices
- Chọn một danh sách đã biên soạn và làm cho hết (Blind 75, Grind 75, NeetCode 150, hoặc CSES theo chủ đề). Sưu tầm tài nguyên là trì hoãn được đóng gói đẹp.
- Luyện theo pattern, không theo bài ngẫu nhiên. Năm tới mười bài trên một pattern, làm liên tiếp. Mục tiêu là hình dạng của bài toán tự động kích hoạt pattern.
- Luôn viết lại từ đầu sau khi đọc lời giải, ngay trong buổi đó. Rồi lặp lại sau một tuần, chỉ dựa vào dòng ghi chú của bạn. Bước này là nơi việc học thực sự diễn ra và cũng là bước hay bị bỏ nhất.
- Giữ một log với một câu insight cho mỗi bài. Không phải code — mà là insight. “Sort theo thời điểm kết thúc rồi tham lam lấy interval kết thúc sớm nhất còn tương thích.” Nếu bạn không nén được thành một câu thì bạn chưa hiểu nó.
- Giãn cách các lần ôn: 1 ngày, 1 tuần, 1 tháng. Ba lần lặp có giãn cách thắng mười lần lặp dồn cục, và nghiên cứu về điều này không hề mơ hồ.
- Timebox mọi bài (25 phút với medium, 40 phút với hard) và dừng khi bạn ngừng sinh ra ý tưởng mới. Vật lộn có ích cho tới đúng lúc nó biến thành ngâm.
- Nói độ phức tạp thành tiếng với từng bài bạn giải, kể cả khi ngồi một mình. Nó phải thành phản xạ, vì lần nào bạn cũng sẽ bị hỏi.
- Phỏng vấn thử, nói thành tiếng, với một người khác. Vừa nói vừa code là kỹ năng tách biệt với code. Đừng để buổi phỏng vấn thật là lần đầu bạn thử.
- Luôn nêu brute force trước khi tối ưu. Nó gửi sẵn một baseline chạy được vào ngân hàng, cho thấy bạn hiểu đề, và định khung cho phần tối ưu.
- Tự test code trước khi bị yêu cầu, với ví dụ nhỏ cộng các edge case bạn đã nhận diện lúc làm rõ đề. Tự bắt bug của mình được chấm cao hơn là để người khác bắt hộ.
- Ưu tiên bài medium. Đó là phân phối thật của phỏng vấn và là tỷ lệ học-được-trên-thời-gian tốt nhất. Làm đủ bài hard để không bị sốc khi gặp một bài; đừng sống ở đó.
- Đều đặn hơn là dồn dập. Hai giờ mỗi ngày trong hai tháng thắng một đợt nước rút hai tuần. Ghi nhớ là hàm của giãn cách.
- Dùng standard library và nói rõ vì sao.
heapq,bisect,collections.deque,sorted— biết cái gì có sẵn và nó tốn bao nhiêu là tín hiệu chuyên nghiệp; tự viết lại một cái heap dưới áp lực thời gian thì không. - Đừng bỏ bê vòng system design và behavioural nếu bạn phỏng vấn từ mức trên junior. Vòng thuật toán thường lại là vòng dễ qua nhất.
- Dừng học kiến thức mới từ một tuần trước. Củng cố, phỏng vấn thử, và ngủ. Tuần cuối là để truy hồi, không phải để thu nạp.
Tài liệu tham khảo
- roadmap.sh — Data Structures & Algorithms
- LeetCode
- HackerRank
- Codeforces
- AtCoder
- CodeChef
- Edabit
- Exercism
- Codewars
- Project Euler
- InterviewBit
- CSES Problem Set
- Advent of Code
- NeetCode — curated problem lists
- Big-O Cheat Sheet
- VisuAlgo — algorithm visualisations
- cp-algorithms
- MIT 6.006 — Introduction to Algorithms (OpenCourseWare)
- Spaced repetition — Wikipedia
- Testing effect (retrieval practice) — Wikipedia
Part of the Data Structures & Algorithms Roadmap knowledge base.
Overview
Everything in the previous twenty-three topics is knowledge you can acquire by reading. This one is not. Data structures and algorithms are a skill, and skills are acquired by doing the thing badly, getting feedback, and doing it again — not by reading about people who do it well.
The mistake almost everyone makes is treating practice as a volume problem: “I’ll do 300 problems.” Volume without structure produces people who have seen 300 problems and can solve about 40 of them, because they solved the other 260 by reading the editorial after twenty minutes and feeling like they understood it. Understanding a solution and being able to produce one are different skills with a nine-month gap between them, and only one of them is what an interview or a real deadline tests.
What works is smaller and less glamorous: learn one pattern, drill it on five to ten problems until the pattern is automatic, then move on and come back a week later. Twelve patterns at ten problems each is 120 problems, which is fewer than most people grind, and it produces a far better result — because after it you recognize the shape of a problem instead of trying to remember whether you have seen this exact one before.
This note covers where to practice, how to practice, in what order, and how to convert that practice into a passed interview. The technical content it draws on lives in the pattern notes: dynamic programming, two pointers and sliding window, and algorithmic complexity, which is the one you will be asked to talk about in every single interview regardless of the problem.
Fundamentals
The platforms, and what each is actually for
Platforms are not interchangeable. Each one optimizes for something different, and using the wrong one for your goal wastes months.
| Platform | Best for | Format | Notes |
|---|---|---|---|
| LeetCode | Software engineering interviews | Individual problems, tagged by company and topic | The default, and correctly so. Editorial quality varies; the discussion tab is often better |
| HackerRank | Structured tracks, screening tests | Problems + certifications + contests | Many companies use it for automated first-round screens, so it is worth being fluent in its I/O style |
| Codeforces | Competitive programming, thinking speed | Rated contests, Div. 1–4 | The strongest problem-setting in the industry. Overkill for interviews, excellent for raw ability |
| AtCoder | Clean, well-graded difficulty ramp | Weekly Beginner/Regular contests | ABC contests are the best-curated ladder for going from “can loop” to “can think”. Editorials in English |
| CodeChef | Long contests, structured learning paths | Contests + practice ladders | Long Challenge format rewards research and persistence over speed |
| Edabit | Absolute beginners, syntax fluency | Very small, gamified exercises | Not algorithm practice. It is the right tool for the “still fighting the syntax” stage in programming fundamentals |
| Exercism | Language fluency + human code review | Exercises per language, mentor feedback | The mentorship is the point. Best for “I want to write idiomatic Go”, not “I want to pass an interview” |
| Codewars | Habit-building, small daily reps | Kata ranked by kyu, community solutions | Comparing your solution to the community’s top-voted ones after solving is genuinely instructive |
| Project Euler | Mathematical problem solving | Number-theory-flavoured problems, no time limits | Not interview prep. Excellent for the maths half of algorithmic thinking |
| InterviewBit | Guided interview prep path | Topic-ordered curriculum with time limits | The curriculum ordering is its value — someone already decided what to study when |
| CSES Problem Set | Systematic algorithm coverage | 300 problems by topic, no editorials | The best free structured list in existence. If you solve all of “Dynamic Programming” and “Graph Algorithms”, you know DP and graphs |
| Advent of Code | Fun, practical problem solving | 25 December days, two parts each | Puzzles, not interview questions — parsing, simulation, and grids. Great for keeping the habit alive in December |
If you want a single recommendation: LeetCode for interview preparation, CSES for building actual algorithmic depth, Codeforces or AtCoder if you find you enjoy this and want to get genuinely good. Everything else is optional.
A note on the “Blind 75” / “NeetCode 150” / “Grind 75” curated lists: they are excellent and you should use one. Their value is not the problems (which are just LeetCode problems) but the curation — someone competent removed the redundancy and the noise, so 75 problems cover the same pattern space that 400 random ones would. Pick one list and finish it; do not collect lists.
The practice loop
Here is the loop that actually builds skill, per problem:
- Read the problem twice. Restate it in your own words. Write down the input constraints — they are the single strongest hint about the intended complexity (see the recognition heuristics in dynamic programming).
- Set a timer. 25 minutes for easy/medium, 40 for hard. Think without writing code for the first 5–10 of them.
- Solve it, or fail cleanly. “Fail cleanly” means: when the timer expires, write down what you tried and where you got stuck, in one sentence, before opening anything.
- Read the editorial — properly. Not “skim until it clicks”. Read until you can explain the key insight without looking. The key insight is usually one sentence (“sort by end time”, “the state is the prefix of each string”, “move the shorter line”). Write that sentence down.
- Close everything and implement it from scratch. This step is not optional and it is the step everyone skips. Reading a solution creates the illusion of competence; reproducing it from the one-sentence insight is what creates the competence.
- Log it. Problem name, pattern, the one-sentence insight, and how it went. A spreadsheet or a plain text file is fine.
- Re-solve it from scratch a week later, from the log entry alone. If you cannot, you did not learn it — you recognized it. Re-read and re-schedule.
Step 7 is spaced repetition, and it is the single highest-leverage habit in this whole note. The schedule that works: 1 day, 1 week, 1 month. Three repetitions at expanding intervals converts a solution from “I saw that once” into recall you can rely on under pressure. Anki works well for this if you already use it; the card front is the problem name plus constraints, the back is your one-sentence insight.
Timeboxing, and why struggling has a limit
The 25-minute timer is a compromise between two failure modes. Give up after five minutes and you never develop the ability to sit with an unsolved problem, which is precisely the ability an interview tests. Refuse to look at the editorial for three hours and you spend a whole evening learning one thing instead of five.
The right rule: struggle productively until you stop generating new ideas, then stop. If you have spent ten minutes cycling through the same three failed approaches, you are not learning, you are marinating. Look it up, extract the insight, and reimplement — the reimplementation is where the learning is.
A useful middle step before the full editorial: read only the tags or only the first paragraph of the editorial. “This is a sliding window problem” is often all the unlock you need, and you keep most of the value of solving it yourself.
Key Concepts
A suggested order through this section
Order matters more than most people think, because roughly half of these topics are prerequisites for the other half. Attempting graphs before you are comfortable with queues and recursion means learning three things at once and none of them well.
Phase 1 — Foundations (do not skip, do not linger).
- Programming fundamentals & pseudocode — only if you are not yet fluent in your language. Edabit or Exercism are the right tools here.
- What are data structures and algorithmic complexity. Complexity is the vocabulary of every later discussion and every interview. Learn it properly now.
- Arrays, linked lists, stacks and queues, hash tables. About 80% of interview problems are solved with an array, a hash map, or both.
Phase 2 — The core algorithmic toolkit.
- Sorting and searching. Binary search deserves more time than it gets — especially “binary search on the answer”, which is the most under-practised high-yield technique on this list.
- Two pointers and sliding window. Yes, out of numerical order — it depends only on arrays, it appears constantly, and it is the fastest confidence win available. Do it here.
- Recursion and divide and conquer. Everything after this assumes you are comfortable with recursion.
Phase 3 — Trees and graphs.
- Tree data structures — traversals, BSTs. Then heaps and priority queues, which unlock the top-k and merge-k families.
- Graph data structures — BFS, DFS, connected components, topological sort. This is where a large share of “medium/hard” problems live.
- Shortest paths, minimum spanning trees, union-find.
Phase 4 — Problem-solving techniques.
- Brute force, greedy, and randomised algorithms, then backtracking.
- Dynamic programming. Last, deliberately: it is the hardest to learn and it builds directly on recursion and memoization. Attempting DP first is the most common reason people conclude they are “bad at algorithms”.
Phase 5 — Depth, if you want it.
- Balanced and multiway trees, advanced tree structures (trie, segment tree, Fenwick), indexing. Tries appear in interviews reasonably often; segment trees almost never, but they are common in competitive programming.
For interview prep specifically, Phases 1–4 are the syllabus. Phase 5 is for depth and for competitive programming.
The pattern list
The reason to organize practice by pattern rather than by topic is that patterns are what you actually recognize under time pressure. This is the list worth internalizing — twelve to fifteen patterns cover the overwhelming majority of interview problems:
| Pattern | Trigger | Where it is covered |
|---|---|---|
| Hashing for O(1) lookup | ”Have I seen this before”, complements, counting | ./07-hash-tables.md |
| Two pointers (opposite) | Sorted array, pairs, palindromes | ./23-two-pointers-and-sliding-window.md |
| Sliding window | Contiguous subarray/substring with a constraint | ./23-two-pointers-and-sliding-window.md |
| Fast & slow pointers | Linked list cycles, middle, n-th from end | ./23-two-pointers-and-sliding-window.md |
| Prefix sums | Range aggregates, subarray sums with negatives | ./23-two-pointers-and-sliding-window.md |
| Binary search (incl. on the answer) | Sorted input, or a monotone predicate over a range | ./09-search-algorithms.md |
| Merge intervals / sweep line | Overlapping ranges, scheduling, concurrency peaks | ./23-two-pointers-and-sliding-window.md |
| Heap / top-k / two heaps | ”k largest”, “median of a stream”, merge k lists | ./12-heaps-and-priority-queues.md |
| Monotonic stack | ”Next greater element”, histogram, spans | ./06-stacks-and-queues.md |
| Tree DFS / BFS | Anything on a tree; level-order; path sums | ./10-tree-data-structures.md |
| Graph traversal + topological sort | Grids, islands, dependencies, cycle detection | ./13-graph-data-structures.md |
| Union-find | Connectivity, grouping, Kruskal | ./16-disjoint-set-union-find.md |
| Backtracking | ”Generate all”, subsets, permutations, sudoku | ./21-backtracking.md |
| Dynamic programming | Count/max/min over choices, greedy provably fails | ./22-dynamic-programming.md |
| Greedy + exchange argument | Local choice provably optimal (prove it!) | ./20-brute-force-greedy-and-randomised-algorithms.md |
Drill one row at a time. Five to ten problems on a single pattern, done consecutively, teaches you the pattern’s shape — the variations, the edge cases, the moment it stops applying. Ten random problems across ten patterns teaches you ten disconnected facts.
Communicating in a technical interview
An interview is not a problem-solving test. It is a simulation of working with you, and the code is the artifact, not the point. Candidates who solve the problem silently and correctly are routinely rated below candidates who solve it collaboratively with a hint. The protocol below is what interviewers are trained to look for.
1. Clarify before you code (2–3 minutes). Ask about input size and ranges, empty and single-element inputs, duplicates, negative numbers, whether the input is sorted, whether it can be modified in place, what to return when there is no answer, and whether the input fits in memory. Restate the problem in your own words and confirm.
This is not a ritual. Interview problems are deliberately underspecified, and asking is how you find out that the array is sorted (which unlocks two pointers), or that values are in 1..n (which unlocks cyclic sort), or that n ≤ 20 (which unlocks bitmask DP). The constraints are half the solution.
2. Work an example by hand. Take a small input and produce the correct output manually. This catches misunderstandings immediately and often reveals the structure of the solution. It also gives you a test case for later.
3. State the brute force, with its complexity, and then say you can do better. Something like: “The obvious approach is to check every pair, which is O(n²) time and O(1) space. Let me see if I can do better.” This is a strictly dominant move. It shows you understand the problem, it puts a working baseline on the table in case you run out of time, and it establishes the complexity you are trying to beat. Never sit in silence hunting for the optimal solution — a stated brute force is worth more than an unstated better idea.
4. Think out loud while you optimize. Say what you are considering and why you are rejecting it: “I could sort, which gives O(n log n), but the problem says the original indices matter, so I’d need to keep them — let me try a hash map instead.” Rejected approaches with reasons are evidence of judgement, which is exactly what is being assessed. Silence is unreadable and gets scored as being stuck.
5. Agree on the approach before writing code. “So: hash map from value to index, one pass, O(n) time and O(n) space. Shall I code that?” If the interviewer is going to redirect you, this is when they will — and it saves you fifteen minutes of writing the wrong thing.
6. Write clean code and narrate. Real names, not a/b/tmp. Handle the edge cases you identified in step 1. Say what you are doing as you do it, but do not narrate syntax — narrate intent (“now I’m shrinking the window until it’s valid again”).
7. Test it yourself, before being asked. Trace through your own code with the small example from step 2, out loud, line by line, watching the variables. Then run the edge cases: empty, one element, all-identical, the target at the boundary. Finding your own bug is a positive signal — it demonstrates exactly the self-checking you would apply on the job. Being told about your bug is a neutral-to-negative one.
8. State the final complexity and any trade-offs. “O(n) time, O(n) space. If memory were tight I’d sort first and use two pointers — O(n log n) time but O(1) extra space.” Naming the alternative you did not take, and why, is a senior-level signal that costs you fifteen seconds.
Common failure modes
These are the ways prepared people still fail, roughly ordered by how often they happen.
- Grinding volume without patterns. 400 problems solved once each, none of them retained. The fix is the pattern list plus spaced repetition.
- Reading the solution and moving on. This is the big one. Recognition is not recall. If you did not close the tab and reimplement, you did not learn it.
- Never practising out loud. Talking and coding simultaneously is a distinct motor skill and it is genuinely hard the first time. If your first attempt is in the real interview, it will go badly. Practise narrating even when alone — mock interviews with a friend, or Pramp/interviewing.io, are better.
- Jumping straight to code. No clarification, no example, no stated approach. You end up solving a problem the interviewer did not ask, and discovering it at minute twenty.
- Chasing the optimal solution and shipping nothing. A working
O(n²)with an explanation of theO(n)you did not have time to write beats an unfinishedO(n). - Ignoring edge cases. Empty input, one element, all duplicates, integer overflow (in languages other than Python), the target at index 0 or
n−1. Interviewers keep a mental checklist and tick it. - Being unable to state your own complexity. If you cannot say why your loop is
O(n log n), nothing else you said counts. Reread algorithmic complexity. - Studying only the hardest problems. Hard problems have poor learning-per-hour. Mediums are the interview distribution and the best training signal.
- Practising in an IDE with autocomplete. Then getting a plain text editor or a whiteboard on the day. Practise in the environment you will be tested in at least some of the time.
- Neglecting the non-algorithmic rounds. For most senior roles, system design and behavioural rounds carry as much or more weight than the algorithms round. Budget your preparation accordingly.
- Burning out three weeks before the interview. Two focused hours a day for eight weeks beats eight hours a day for two weeks, by a wide margin, because retention is a function of spacing, not of total hours.
The last week before an interview
The week before is not for learning new material. Anything you have not learned by now will not be reliable in seven days, and cramming displaces the retrieval practice that would actually help.
7 days out. Stop new topics. Re-solve 20–30 problems you have already done, one or two from each pattern, from your log. Speed matters here — you are rebuilding recall, not understanding.
5 days out. Do two full mock interviews with a real human, out loud, timed, on a shared editor with no autocomplete. This is the single highest-value activity of the whole week and the one people avoid because it is uncomfortable. That discomfort is the point.
3 days out. Go through your log and list the patterns where you still hesitate. Do three or four problems on each. Reread your one-sentence insights for everything else.
2 days out. Research the company: which round is which, how long, what language you may use, whether it is a shared doc or a real IDE. Prepare your questions for them, and your two or three project stories for the behavioural section.
1 day out. One or two easy problems to stay warm — nothing hard, nothing new. Reread your notes for thirty minutes, not three hours. Then stop, and sleep properly. Sleep does more for problem-solving performance the next day than any amount of last-minute revision, and the effect is not small.
On the day. Warm up with one easy problem an hour before, so the first code you write is not in the interview. Check your setup: camera, microphone, editor, a glass of water, and a pen and paper for diagrams. Have your clarifying questions ready as a checklist — under stress you will forget to ask, and asking is worth more than any single line of code you will write.
Best Practices
- Choose one curated list and finish it (Blind 75, Grind 75, NeetCode 150, or CSES by topic). Collecting resources is procrastination with good branding.
- Practise by pattern, not by random problem. Five to ten problems on one pattern consecutively. The goal is that the shape of the problem triggers the pattern automatically.
- Always reimplement from scratch after reading a solution, in the same session. Then again a week later, from your one-line note only. This step is where the learning happens and it is the one that gets skipped.
- Keep a log with a one-sentence insight per problem. Not the code — the insight. “Sort by end time and greedily take the earliest-ending compatible interval.” If you cannot compress it to one sentence, you do not understand it yet.
- Space your reviews: 1 day, 1 week, 1 month. Three spaced repetitions beat ten massed ones, and the research on this is not ambiguous.
- Timebox every problem (25 minutes medium, 40 hard) and stop when you stop generating new ideas. Struggle is productive right up until it becomes marination.
- Say your complexity out loud for every single problem you solve, even alone. It has to be automatic, because you will be asked every time.
- Do mock interviews out loud with another person. Talking while coding is a separate skill from coding. Do not let the interview be the first time you try it.
- State the brute force before optimizing, always. It banks a working baseline, demonstrates comprehension, and frames the optimization.
- Test your own code before being asked, with the small example plus the edge cases you identified during clarification. Catching your own bug scores better than having it caught for you.
- Prefer mediums. They are the interview distribution and the best ratio of learning to time spent. Do enough hards to not be shocked by one; do not live there.
- Consistency over intensity. Two hours daily for two months beats a two-week sprint. Retention is a function of spacing.
- Use your standard library and say why.
heapq,bisect,collections.deque,sorted— knowing what exists and what it costs is a professional signal; reimplementing a heap under time pressure is not. - Do not neglect system design and behavioural rounds if you are interviewing above junior level. The algorithms round is often the easiest one to pass.
- Stop learning new material a week out. Consolidate, mock, sleep. The last week is for retrieval, not acquisition.
References
- roadmap.sh — Data Structures & Algorithms
- LeetCode
- HackerRank
- Codeforces
- AtCoder
- CodeChef
- Edabit
- Exercism
- Codewars
- Project Euler
- InterviewBit
- CSES Problem Set
- Advent of Code
- NeetCode — curated problem lists
- Big-O Cheat Sheet
- VisuAlgo — algorithm visualisations
- cp-algorithms
- MIT 6.006 — Introduction to Algorithms (OpenCourseWare)
- Spaced repetition — Wikipedia
- Testing effect (retrieval practice) — Wikipedia