CS FUNDAMENTALS + DSA + VÒNG CODE — Ôn tập cho Fresher Backend (VPBank)
1. Danh sách chủ đề con theo độ ưu tiên
Ưu tiên CAO (gần như chắc chắn bị hỏi)
- Big-O notation: định nghĩa, các mức phổ biến O(1), O(log n), O(n), O(n log n), O(n²); phân biệt time vs space complexity; biết ước lượng complexity của code mình vừa viết (interviewer VN hầu như luôn hỏi "độ phức tạp bài này là bao nhiêu?").
- Array/String manipulation: reverse string, đếm ký tự, tìm phần tử xuất hiện nhiều nhất, xóa duplicate, kiểm tra palindrome, tìm max/min/second max.
- HashMap/HashSet: nguyên lý hashing, collision, dùng để đếm tần suất và tìm cặp (Two Sum) — đây là "vũ khí" số 1 để đưa O(n²) về O(n).
- Sorting + độ phức tạp: nắm ý tưởng Bubble/Selection/Insertion (O(n²)), Quick/Merge sort (O(n log n)); stable vs unstable; sort của ngôn ngữ dùng gì (Java: Dual-Pivot Quicksort cho primitive, TimSort cho object).
- Binary search: điều kiện áp dụng (mảng đã sort), code đúng không lệch biên, O(log n).
- Stack/Queue: LIFO vs FIFO, bài valid parentheses, ứng dụng thực tế (call stack, message queue, undo).
- Networking cốt lõi: HTTP vs HTTPS, TCP vs UDP, HTTP methods + idempotency, status codes nhóm 2xx/4xx/5xx.
- OS cốt lõi: process vs thread; deadlock và 4 điều kiện.
Ưu tiên TRUNG BÌNH
- Two pointers / sliding window: remove duplicates in-place, tìm cặp trong mảng đã sort, substring không lặp ký tự.
- Đệ quy (recursion): base case, giai thừa/Fibonacci, nguy cơ StackOverflow, memoization sơ đẳng.
- Linked list cơ bản: reverse linked list, phát hiện cycle (fast/slow pointer), so sánh với array.
- Cookie vs Session vs JWT: cơ chế, nơi lưu, ưu nhược — rất hay hỏi ở ngân hàng vì liên quan authentication.
- HTTPS/TLS handshake mức khái niệm: asymmetric để trao đổi key, symmetric để mã hóa dữ liệu, certificate/CA.
- Complexity của thao tác trên từng cấu trúc: get/insert/delete của array, linked list, HashMap, balanced BST (bảng so sánh).
Ưu tiên THẤP (biết khái niệm là đủ, ít khi đào sâu với fresher)
- Tree/BST traversal (inorder/preorder/postorder), BFS/DFS mức nhận diện.
- Dynamic programming (chỉ mức Fibonacci/climbing stairs).
- Heap/PriorityQueue (biết dùng cho top-K).
- DNS, 3-way handshake chi tiết từng flag, OSI 7 layers (thuộc tên layer là đủ).
- Semaphore/mutex chi tiết, scheduling algorithms.
2. Câu hỏi có khả năng cao + hướng trả lời
Nhóm DSA
Q1. Big-O là gì? Vì sao O(n log n) tốt hơn O(n²)?
- Big-O mô tả tốc độ tăng của thời gian/bộ nhớ theo kích thước input, ở trường hợp xấu nhất, bỏ hằng số và bậc thấp.
- Cho ví dụ: n = 1 triệu → n² = 10¹² phép tính (không chạy nổi), n log n ≈ 2×10⁷ (chạy tốt).
- Ăn điểm: nói được cả space complexity, và tự áp dụng vào code vừa viết mà không cần bị hỏi.
- Lỗi cần tránh: nói "Big-O là thời gian chạy thực tế" (sai — là tốc độ tăng trưởng); quên rằng 2 vòng for lồng nhau trên cùng mảng là O(n²) nhưng 2 vòng for tách rời là O(n).
Q2. Array và Linked List khác nhau thế nào? Khi nào dùng cái nào?
- Array: cấp phát liên tục, truy cập theo index O(1), insert/delete giữa mảng O(n).
- Linked list: node rời rạc nối bằng pointer, truy cập O(n), insert/delete tại vị trí đã biết O(1).
- Array thân thiện cache (locality) → thực tế thường nhanh hơn.
- Ăn điểm: nhắc ArrayList vs LinkedList trong Java và nói "thực tế 99% dùng ArrayList vì cache locality".
- Lỗi cần tránh: nói "insert vào LinkedList luôn O(1)" — chỉ đúng khi đã có reference tới node, còn tìm vị trí vẫn O(n).
Q3. HashMap hoạt động như thế nào? Collision xử lý ra sao?
- Key → hashCode → index bucket; get/put trung bình O(1).
- Collision: chaining (Java dùng linked list, từ Java 8 chuyển sang red-black tree khi bucket dài > 8 → worst case O(log n)).
- Load factor (mặc định 0.75) → resize/rehash khi đầy.
- Ăn điểm: nêu contract
hashCode()/equals() — hai object equals thì hashCode phải bằng nhau; hậu quả nếu vi phạm (mất phần tử trong map).
- Lỗi cần tránh: chỉ nói "HashMap là key-value" mà không nói được cơ chế bucket; nói worst case là O(1).
Q4. Stack và Queue khác nhau thế nào? Cho ví dụ ứng dụng.
- Stack: LIFO — call stack của chương trình, undo, kiểm tra ngoặc hợp lệ, DFS.
- Queue: FIFO — hàng đợi xử lý request, BFS, message queue (Kafka/RabbitMQ).
- Ăn điểm (góc ngân hàng): nói queue là nền của xử lý giao dịch bất đồng bộ trong banking — giao dịch xếp hàng vào message queue để đảm bảo xử lý tuần tự, không mất.
- Lỗi cần tránh: lẫn lộn LIFO/FIFO khi bị hỏi nhanh; không nêu được ứng dụng thực tế nào.
Q5. Viết binary search. Điều kiện áp dụng là gì?
- Điều kiện: dữ liệu đã sort (hoặc có tính đơn điệu).
- Mỗi bước loại nửa không gian tìm kiếm → O(log n).
- Template:
left <= right, mid = left + (right - left) / 2, cập nhật left = mid + 1 / right = mid - 1.
- Ăn điểm: viết
mid = left + (right - left) / 2 và giải thích tránh integer overflow — chi tiết nhỏ nhưng interviewer đánh giá cao.
- Lỗi cần tránh: sai điều kiện dừng gây vòng lặp vô hạn; quên nói điều kiện "mảng phải sort"; viết
(left + right) / 2 mà không biết nguy cơ overflow.
Q6. So sánh Quick sort và Merge sort. Sort nào là stable?
- Quick sort: trung bình O(n log n), worst O(n²) (pivot xấu), in-place, không stable.
- Merge sort: luôn O(n log n), cần O(n) bộ nhớ phụ, stable.
- Stable = giữ thứ tự tương đối của phần tử bằng nhau — quan trọng khi sort nhiều tiêu chí (ví dụ sort giao dịch theo số tiền, các giao dịch cùng số tiền vẫn giữ thứ tự thời gian).
- Ăn điểm: biết
Arrays.sort() trong Java dùng gì (Quicksort biến thể cho primitive, TimSort — stable — cho object) và giải thích vì sao object cần stable sort.
- Lỗi cần tránh: nói Quick sort "luôn O(n log n)"; không giải thích được stable nghĩa là gì.
Q7. Đệ quy là gì? Khi nào không nên dùng?
- Hàm tự gọi chính nó; bắt buộc có base case để dừng.
- Mỗi lần gọi tốn 1 stack frame → đệ quy quá sâu gây StackOverflowError.
- Fibonacci đệ quy thuần là O(2ⁿ) → fix bằng memoization hoặc vòng lặp O(n).
- Ăn điểm: tự chỉ ra Fibonacci naive là O(2ⁿ) và cách tối ưu — đây là bẫy kinh điển interviewer chờ ứng viên tự nhận ra.
- Lỗi cần tránh: viết đệ quy thiếu base case; không trả lời được "độ sâu đệ quy tối đa bao nhiêu thì lỗi" (phụ thuộc stack size, thường vài nghìn đến vài chục nghìn frame).
Q8. Bài Two Sum: tìm 2 số trong mảng có tổng bằng target.
- Cách brute force: 2 vòng for O(n²) — nêu ra trước rồi tự đề xuất tối ưu.
- Cách tối ưu: duyệt 1 lần, với mỗi phần tử
x kiểm tra target - x đã có trong HashMap chưa → O(n) time, O(n) space.
- Nếu mảng đã sort: two pointers O(n) time, O(1) space.
- Ăn điểm: trình bày trade-off giữa các cách (time vs space) thay vì chỉ code 1 cách; hỏi lại điều kiện (có duplicate không, có nhiều đáp án không).
- Lỗi cần tránh: nhảy vào code ngay không hỏi lại đề; quên xử lý case phần tử dùng 2 lần (ví dụ target=6, x=3).
Nhóm Networking
Q9. HTTP và HTTPS khác nhau thế nào?
- HTTP: plain text, port 80, ai bắt gói tin đều đọc được.
- HTTPS = HTTP + TLS: mã hóa (confidentiality), chống sửa đổi (integrity), xác thực server qua certificate (authentication).
- Ăn điểm (góc ngân hàng): nói rõ "banking bắt buộc HTTPS toàn bộ — số tài khoản, OTP, mật khẩu đi qua HTTP là lộ ngay khi dùng chung Wi-Fi"; nhắc thêm chống man-in-the-middle.
- Lỗi cần tránh: nói "HTTPS chỉ là HTTP có mã hóa" mà bỏ quên 2 vai trò còn lại (integrity + authentication qua certificate).
Q10. TCP vs UDP? Hệ thống ngân hàng dùng cái nào?
- TCP: connection-oriented (3-way handshake), đảm bảo thứ tự, retransmit khi mất gói, có flow/congestion control → tin cậy nhưng chậm hơn.
- UDP: connectionless, không đảm bảo delivery/thứ tự → nhanh, dùng cho streaming, game, DNS.
- Ăn điểm (góc ngân hàng): "Giao dịch tài chính không được phép mất hay lặp gói tin → HTTP/HTTPS của API banking chạy trên TCP. Mất 1 gói UDP khi xem video thì giật hình, mất 1 gói của lệnh chuyển tiền thì sai tiền."
- Lỗi cần tránh: nói "UDP là giao thức lỗi thời/không ai dùng"; không nêu được ví dụ ứng dụng cho từng loại.
Q11. Các HTTP method và tính idempotent? Vì sao idempotency quan trọng?
- GET (đọc), POST (tạo), PUT (thay thế toàn bộ), PATCH (sửa một phần), DELETE (xóa).
- Idempotent = gọi N lần cho kết quả trên server như gọi 1 lần: GET/PUT/DELETE idempotent, POST thì không.
- Ăn điểm (góc ngân hàng — câu ăn điểm lớn nhất mảng này): "POST /transfer không idempotent — nếu client timeout rồi retry, tiền có thể bị trừ 2 lần. Ngân hàng giải quyết bằng idempotency key: client gửi kèm unique transaction ID, server check trùng thì không xử lý lại." Fresher nói được ý này thường gây ấn tượng mạnh.
- Lỗi cần tránh: nói "GET và POST chỉ khác chỗ tham số trên URL hay trong body"; nhầm idempotent với "không thay đổi dữ liệu" (DELETE thay đổi dữ liệu nhưng vẫn idempotent).
Q12. Kể các HTTP status code hay gặp.
- 2xx thành công: 200 OK, 201 Created, 204 No Content.
- 4xx lỗi client: 400 Bad Request, 401 Unauthorized (chưa xác thực), 403 Forbidden (không đủ quyền), 404 Not Found, 409 Conflict, 429 Too Many Requests.
- 5xx lỗi server: 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout.
- Ăn điểm: phân biệt rõ 401 vs 403 (authentication vs authorization) — câu hỏi phụ kinh điển; nhắc 429 dùng cho rate limiting API banking chống brute-force OTP.
- Lỗi cần tránh: nhầm 401/403; nói 404 là lỗi server.
Q13. Cookie vs Session vs JWT? App ngân hàng nên dùng gì?
- Cookie: nơi lưu dữ liệu nhỏ phía browser, tự động gửi kèm request — là phương tiện vận chuyển, thường chứa session ID.
- Session: state lưu phía server, client chỉ giữ session ID → server kiểm soát được, revoke ngay được, nhưng khó scale (cần sticky session hoặc Redis).
- JWT: token tự chứa thông tin (header.payload.signature), server không cần lưu state → stateless, dễ scale microservices; nhược điểm: khó revoke trước khi hết hạn, payload chỉ được encode (Base64) chứ không mã hóa.
- Ăn điểm (góc ngân hàng): "JWT nên để expiry ngắn + refresh token; không nhét dữ liệu nhạy cảm (số dư, số tài khoản) vào payload vì ai cũng decode được; cookie nên set HttpOnly + Secure + SameSite để chống XSS/CSRF."
- Lỗi cần tránh: nói "JWT được mã hóa nên an toàn" (sai — chỉ được ký/sign, không encrypt); nói cookie và session là hai lựa chọn thay thế nhau (thực ra cookie thường chở session ID).
Q14. HTTPS/TLS handshake diễn ra thế nào (mức khái niệm)?
- Client hello (gửi TLS version, cipher suites hỗ trợ) → Server gửi certificate chứa public key.
- Client verify certificate với CA (Certificate Authority) → tin server là thật.
- Hai bên dùng asymmetric encryption để thống nhất một symmetric session key.
- Sau handshake, toàn bộ dữ liệu mã hóa bằng symmetric key (nhanh hơn nhiều).
- Ăn điểm: giải thích được VÌ SAO kết hợp cả hai: asymmetric an toàn để trao key nhưng chậm, symmetric nhanh để truyền dữ liệu — nắm được "why" là đủ cho fresher.
- Lỗi cần tránh: cố học thuộc từng message của handshake rồi kể sai thứ tự; nói toàn bộ phiên dùng asymmetric encryption.
Nhóm OS
Q15. Process vs Thread?
- Process: instance của chương trình đang chạy, có memory space riêng, cách ly nhau; context switch tốn kém.
- Thread: đơn vị thực thi trong process, các thread cùng process chia sẻ heap/memory, mỗi thread có stack riêng; tạo và switch nhẹ hơn.
- Chia sẻ memory → giao tiếp nhanh nhưng sinh race condition, cần synchronization.
- Ăn điểm (góc ngân hàng): dẫn tới ví dụ "2 thread cùng cập nhật số dư 1 tài khoản không có lock → đọc-sửa-ghi chồng lên nhau, mất tiền" — nối được OS với bài toán concurrency trong banking.
- Lỗi cần tránh: nói "thread có memory hoàn toàn riêng" (stack riêng nhưng heap chung); không nêu được vì sao chia sẻ memory vừa là ưu vừa là nhược.
Q16. Deadlock là gì? 4 điều kiện xảy ra deadlock? Cách phòng tránh?
- Deadlock: 2+ process/thread chờ tài nguyên của nhau mãi mãi, không ai tiến được.
- 4 điều kiện (phải xảy ra ĐỒNG THỜI): Mutual exclusion, Hold and wait, No preemption, Circular wait.
- Phòng tránh: phá 1 trong 4 điều kiện — thực dụng nhất là phá circular wait bằng lock ordering (mọi thread lấy lock theo cùng một thứ tự).
- Ăn điểm (góc ngân hàng — ví dụ kinh điển): "Transaction 1 chuyển tiền A→B khóa account A rồi chờ B; transaction 2 chuyển B→A khóa B rồi chờ A → deadlock. Giải pháp: luôn lock theo thứ tự account ID tăng dần." Ví dụ này đúng trọng tâm ngân hàng, nên chuẩn bị kỹ.
- Lỗi cần tránh: chỉ thuộc lòng 4 tên điều kiện mà không giải thích được từng cái; không đưa được ví dụ hay cách phòng tránh cụ thể nào.
3. Vòng coding test online + coding trước interviewer
Dạng bài thường gặp (HackerRank/CodeSignal/TestDome, easy → medium)
- String: reverse, palindrome, đếm ký tự/từ, anagram check, tìm ký tự không lặp đầu tiên, nén chuỗi ("aaabb" → "a3b2").
- Array + HashMap: Two Sum, phần tử xuất hiện quá n/2 lần (majority element), số xuất hiện lẻ lần, merge 2 mảng đã sort, tìm missing number 1..n.
- Stack: valid parentheses, min stack.
- Two pointers/sliding window: remove duplicates in-place, longest substring without repeating characters, cặp có tổng = target trong mảng sorted.
- Đệ quy/toán: Fibonacci, giai thừa, kiểm tra số nguyên tố, FizzBuzz (vẫn được dùng để lọc).
- Linked list: reverse, detect cycle, tìm node giữa.
- SQL kèm theo (rất phổ biến trong test online của bank): JOIN, GROUP BY + HAVING, tìm top-N, lương cao thứ 2.
- Một số bank/outsourcing dùng đề dạng "fix bug trong đoạn code cho sẵn" hoặc trắc nghiệm Java/OOP/SQL trộn lẫn — đọc kỹ format đề trước khi thi.
Chiến lược làm bài test online
- Đọc hết tất cả bài trước khi code; làm bài dễ nhất trước để chốt điểm, phân bổ thời gian theo điểm số từng bài.
- Đọc kỹ constraint: n ≤ 10³ → O(n²) chấp nhận được; n ≥ 10⁵ → phải O(n log n) trở xuống. Constraint chính là gợi ý lời giải.
- Chạy được brute force còn hơn tối ưu dở dang: đa số platform chấm theo test case pass — giải O(n²) pass 70% test vẫn hơn giải O(n) không compile.
- Tự test edge case trước khi submit: mảng rỗng, 1 phần tử, toàn phần tử giống nhau, số âm, giá trị lớn nhất (integer overflow — dùng
long khi tính tổng/tích).
- Cẩn thận I/O format: sai một dòng println thừa là fail toàn bộ test case.
- Nếu bank cho chọn ngôn ngữ, chọn ngôn ngữ mình apply (Java cho backend VPBank) — interviewer vòng sau có thể xem lại code.
Coding trước interviewer: quy trình + "think aloud"
Quy trình 6 bước (nói ra thành lời từng bước — im lặng code là lỗi lớn nhất):
- Nhắc lại đề + hỏi làm rõ: "Em xác nhận lại: input là mảng int, có thể có số âm không ạ? Có duplicate không? Output cần index hay value?" — hỏi 1-2 câu clarify luôn được cộng điểm.
- Nêu brute force trước: "Cách đơn giản nhất là 2 vòng for, O(n²). Em nêu ra trước rồi sẽ tối ưu." — chứng minh mình luôn có lời giải chạy được.
- Đề xuất tối ưu + trade-off: "Em có thể đánh đổi O(n) memory dùng HashMap để đưa time về O(n)."
- Xác nhận hướng đi rồi mới code: "Em code theo hướng HashMap nhé anh/chị?" — tránh code 15 phút theo hướng interviewer không muốn.
- Vừa code vừa nói: "Em duyệt mảng, với mỗi phần tử em check complement trong map..." — code sạch, tên biến có nghĩa (
seenNumbers thay vì m).
- Tự test + tự nêu complexity: dry-run bằng 1 ví dụ nhỏ trên giấy/màn hình, tự chỉ ra edge case, kết bằng "Time O(n), space O(n)" mà KHÔNG cần interviewer hỏi.
Khi bị bí:
- Nói ra hướng đang nghĩ thay vì im lặng: "Em đang cân nhắc giữa sort trước hay dùng HashMap..." — interviewer chấm quá trình tư duy, không chỉ đáp án, và thường sẽ gợi ý nếu thấy mình đang suy nghĩ đúng hướng.
- Nhận gợi ý một cách tích cực và dùng ngay — cố chấp bỏ qua hint là red flag.
Lỗi thường gặp cần tránh trong vòng code:
- Nhảy vào code ngay không hỏi clarify, không nêu approach.
- Im lặng suốt lúc code.
- Không tự test — tuyên bố "xong rồi ạ" khi code còn bug lộ liễu.
- Không nói được complexity của chính code mình viết.
- Bị hỏi "có cách nào tốt hơn không?" thì trả lời "hết cách rồi" — luôn thử nghĩ về hướng đánh đổi space/time hoặc tận dụng tính chất input (sorted, bounded range...).
Chi tiết ăn điểm riêng cho ngân hàng trong vòng code: nếu bài liên quan đến tiền, chủ động nói "nếu đây là số tiền thật, em sẽ không dùng float/double vì sai số làm tròn nhị phân — em dùng BigDecimal (Java) hoặc lưu theo đơn vị nhỏ nhất (đồng) bằng long". Câu này cực hợp khẩu vị interviewer ngân hàng và ít fresher nói được.