Thư Viện Câu Hỏi Phỏng Vấn

Tổng hợp các câu hỏi tuyển dụng thực tế theo nhiều cấp độ từ Entry đến Expert để bạn tự tin chinh phục nhà tuyển dụng.

01

Sự khác biệt về cơ chế lưu trữ và hiệu năng giữa ArrayList và LinkedList?

Middle
  • ArrayList: Sử dụng một mảng động (dynamic array) nằm liên tục trong bộ nhớ dưới nền. Truy cập ngẫu nhiên phần tử cực nhanh O(1) qua chỉ số. Tuy nhiên, thao tác thêm/xóa phần tử ở giữa hoặc đầu mảng rất chậm O(n) vì phải sao chép và dịch chuyển các phần tử khác.
  • LinkedList: Sử dụng cấu trúc danh sách liên kết đôi, các node nằm rải rác trong bộ nhớ và liên kết với nhau qua con trỏ. Truy cập ngẫu nhiên chậm O(n) vì phải duyệt từ đầu/cuối danh sách. Tuy nhiên, việc thêm/xóa phần tử tại vị trí bất kỳ cực nhanh O(1) sau khi đã tìm thấy node đó.
  • 02

    Kỹ thuật Hai con trỏ (Two Pointers) được ứng dụng trong những dạng bài toán nào trên mảng? Cho ví dụ?

    Middle

    Kỹ thuật Hai con trỏ sử dụng hai biến trỏ chạy với tốc độ hoặc hướng khác nhau trên mảng để giải quyết bài toán mà không cần lặp lồng nhau, giảm độ phức tạp từ O(n^2) xuống O(n):

    • Ứng dụng phổ biến:
      1. Tìm cặp số có tổng bằng X trên mảng đã sắp xếp: Một con trỏ ở đầu (left = 0) và một ở cuối (right = n-1). Nếu tổng lớn hơn X thì dịch right sang trái, ngược lại dịch left sang phải.
      2. Đảo ngược mảng: Hoán vị hai phần tử ở leftright, sau đó thu hẹp khoảng cách cho đến khi chúng gặp nhau.
      3. Phát hiện chu trình (Fast/Slow pointers).
    03

    So sánh cơ chế đồng thuận Proof of Work (PoW) và Proof of Stake (PoS) trong mạng lưới Blockchain?

    Middle
  • Proof of Work (PoW): Các thợ đào (miners) phải cạnh tranh để giải quyết một câu đố toán học phức tạp bằng sức mạnh phần cứng (hash power). Hệ thống cực kỳ an toàn, phi tập trung tốt nhưng tiêu tốn lượng điện năng khổng lồ và tốc độ giao dịch chậm.
  • Proof of Stake (PoS): Thay thế thợ đào bằng các trình xác thực (validators) - những người khóa một lượng tiền điện tử (hot stake) của họ làm tài sản thế chấp. Hệ thống chọn ngẫu nhiên validator để đóng block dựa trên tỷ lệ stake. Thân thiện với môi trường, tốc độ giao dịch nhanh hơn nhưng có nguy cơ tích lũy quyền lực và lỗ hổng "Nothing at Stake".
  • 04

    Hãy giải thích sự khác nhau về cơ chế hoạt động giữa HashMap và HashTable?

    Middle
  • HashMap: Không đồng bộ (non-synchronized), không an toàn khi chạy đa luồng (non-thread-safe). HashMap cho phép lưu trữ một khóa null và nhiều giá trị null. Tốc độ xử lý nhanh hơn do không tốn chi phí khóa luồng.
  • HashTable: Đồng bộ (synchronized), an toàn khi chạy đa luồng (thread-safe) vì mọi phương thức đều được khóa bằng mutex. Không cho phép bất kỳ khóa hoặc giá trị nào nhận null. Chậm hơn HashMap. Hiện nay, trong đa luồng người ta khuyên dùng ConcurrentHashMap thay vì HashTable.
  • 05

    Làm thế nào để kiểm tra hai chuỗi có phải là Anagram (chuỗi đảo chữ) của nhau hay không với độ phức tạp tối ưu O(n)?

    Middle

    Hai chuỗi là Anagram nếu chúng chứa các ký tự giống hệt nhau với số lần xuất hiện tương đương:

    1. Giải pháp tối ưu: Sử dụng một mảng đếm tần suất (hoặc HashMap) có kích thước 26 (nếu chỉ chứa ký tự chữ thường).
    2. Thuật toán: Duyệt qua chuỗi thứ nhất, tăng số đếm của ký tự tương ứng lên 1. Duyệt qua chuỗi thứ hai, giảm số đếm của ký tự tương ứng đi 1.
    3. Kiểm tra: Nếu sau hai vòng lặp, tất cả các ô trong mảng đếm đều bằng 0 thì hai chuỗi đó là Anagram của nhau. Độ phức tạp thời gian là O(n) và độ phức tạp không gian là O(1) do kích thước mảng đếm cố định.
    06

    Làm thế nào để tìm node nằm ở giữa (middle node) của Danh sách liên kết đơn chỉ trong một lần duyệt duy nhất?

    Middle

    Sử dụng kỹ thuật hai con trỏ chạy với tốc độ khác nhau (Fast & Slow Pointers):

    1. Khởi tạo con trỏ slowfast cùng trỏ vào head.
    2. Duyệt danh sách liên kết: Mỗi bước, di chuyển slow đi 1 bước (slow = slow.next) và fast đi 2 bước (fast = fast.next.next).
    3. Khi con trỏ fast đi đến cuối danh sách (fast == null hoặc fast.next == null), con trỏ slow sẽ dừng đúng ở vị trí node giữa của danh sách liên kết. Thuật toán chỉ duyệt qua danh sách đúng 1 lần.
    07

    Giải thuật Tìm kiếm Nhị phân (Binary Search) hoạt động trên nguyên lý nào? Làm thế nào để tối ưu hóa tránh lỗi tràn số (integer overflow) khi tính chỉ số giữa (mid)?

    Middle

    Tìm kiếm Nhị phân hoạt động trên mảng đã được sắp xếp dựa trên nguyên lý Chia để trị (Divide and Conquer), liên tục chia đôi không gian tìm kiếm:

    1. Cơ chế: So sánh phần tử cần tìm với phần tử ở giữa (mid). Nếu bằng, trả về chỉ số. Nếu nhỏ hơn, thu hẹp phạm vi tìm kiếm về nửa bên trái. Nêu lớn hơn, thu hẹp về nửa bên phải.
    2. Lỗi tràn số (Integer Overflow): Công thức tính thông thường là mid = (low + high) / 2. Nếu lowhigh đều là các số nguyên lớn, tổng của chúng có thể vượt quá giới hạn lưu trữ tối đa của kiểu số nguyên (ví dụ: 2^31 - 1 trong Java/C++), dẫn đến giá trị âm hoặc kết quả sai lệch.
    3. Tối ưu hóa: Thay đổi công thức tính mid thành mid = low + (high - low) / 2. Công thức này đảm bảo không bao giờ xảy ra tràn số vì hiệu high - low luôn là số dương nhỏ hơn giới hạn, và khi cộng vào low cũng không vượt quá high. Độ phức tạp thời gian là O(log n) và không gian là O(1).
    08

    Hãy giải thích cách ứng dụng Cấu trúc dữ liệu Ngăn xếp (Stack) để kiểm tra tính hợp lệ của các dấu ngoặc đóng mở trong một chuỗi biểu thức?

    Middle

    Ngăn xếp hoạt động theo nguyên lý LIFO (Last In, First Out - Vào sau, Ra trước), cực kỳ thích hợp để kiểm tra các cấu trúc lồng nhau như dấu ngoặc.

    • Thuật toán:
      1. Khởi tạo một stack rỗng.
      2. Duyệt qua từng ký tự của chuỗi biểu thức từ trái sang phải.
      3. Nếu gặp dấu ngoặc mở ((, [, {), ta đẩy (push) nó vào stack.
      4. Nếu gặp dấu ngoặc đóng (), ], }):
        • Kiểm tra nếu stack rỗng: Trả về false ngay lập tức (thừa ngoặc đóng).
        • Lấy phần tử đỉnh stack ra (pop) và kiểm tra xem nó có phải là dấu ngoặc mở tương ứng cùng loại hay không. Nếu không khớp, trả về false.
      5. Sau khi duyệt hết chuỗi, nếu stack trống hoàn toàn thì chuỗi hợp lệ (trả về true). Nếu stack vẫn còn phần tử thì chuỗi không hợp lệ (thừa ngoặc mở, trả về false).
    • Độ phức tạp: Thời gian O(n) và không gian O(n) cho việc lưu trữ stack.
    09

    Thuật toán tham lam (Greedy Algorithm) hoạt động dựa trên nguyên lý nào? Tại sao nó không luôn luôn cho ra kết quả tối ưu toàn cục? Cho ví dụ?

    Middle

    Thuật toán tham lam hoạt động trên nguyên lý: Tại mỗi bước đi, luôn đưa ra lựa chọn tốt nhất tại thời điểm hiện tại (lựa chọn tối ưu cục bộ - local optimal) với hy vọng rằng các lựa chọn này sẽ dẫn đến lời giải tối ưu toàn cục (global optimal).

    • Hân chế: Thuật toán tham lam không bao giờ nhìn lại hoặc thay đổi các quyết định đã đưa ra ở bước trước. Nó thiếu cái nhìn tổng thể về không gian trạng thái, do đó dễ bị rơi vào bẫy tối ưu cục bộ mà bỏ lỡ đường đi tốt nhất.
    • Ví dụ (Bài toán thối tiền lẻ): Hệ thống tiền lẻ có các mệnh giá: 1, 3, 4 đô la. Ta cần thối lại số tiền là 6 đô la với số tờ tiền ít nhất.
      • Tiếp cận Tham lam: Chọn tờ lớn nhất trước là 4 -> còn 2 đô la -> chọn 2 tờ 1 đô la. Tổng cộng cần 3 tờ (4, 1, 1).
      • Tiếp cận Tối ưu: Chọn 2 tờ 3 đô la. Tổng cộng chỉ cần 2 tờ (3, 3). Thuật toán tham lam đã thất bại trong việc tìm lời giải tối ưu toàn cục.
    10

    Nguyên lý hoạt động của phương pháp Chia để trị (Divide and Conquer) là gì? Nêu 3 thuật toán kinh điển sử dụng phương pháp này?

    Middle

    Phương pháp Chia để trị giải quyết bài toán phức tạp bằng cách chia nó thành các bài toán con nhỏ hơn cùng loại, giải quyết các bài toán con một cách độc lập, sau đó kết hợp các kết quả đó lại để có lời giải cho bài toán ban đầu.

    • 3 Bước cốt lõi:
      1. Divide (Chia): Chia bài toán lớn thành các bài toán con nhỏ hơn.
      2. Conquer (Trị): Giải quyết các bài toán con một cách đệ quy. Nếu bài toán con đủ nhỏ (Base Case), ta giải quyết trực tiếp.
      3. Combine (Kết hợp): Gộp kết quả của các bài toán con để tạo thành lời giải cuối cùng.
    • 3 Thuật toán kinh điển:
      1. Merge Sort: Chia mảng thành 2 nửa, sắp xếp đệ quy từng nửa rồi gộp hai nửa đã sắp xếp lại trong O(n).
      2. Quick Sort: Phân chia mảng quanh một phần tử chốt (pivot) sao cho bên trái nhỏ hơn pivot và bên phải lớn hơn pivot, sau đó sắp xếp đệ quy hai phần.
      3. Binary Search: Chia đôi mảng đã sắp xếp và chỉ chọn một nửa thích hợp để tiếp tục tìm kiếm đệ quy hoặc lặp.
    11

    Làm thế nào để triển khai và tối ưu hóa thuật toán hoặc cấu trúc dữ liệu liên quan đến Double-ended Queue (Deque) trong chủ đề Queues?

    Middle

    Trong lập trình giải thuật với Queues, việc làm chủ Double-ended Queue (Deque) yêu cầu lập trình viên hiểu rõ cấu trúc vật lý trong bộ nhớ và độ phức tạp tính toán:

    1. Độ phức tạp: Luôn đánh giá Time Complexity (thời gian) và Space Complexity (không gian) tối ưu nhất (ví dụ: tối ưu từ O(n^2) xuống O(n log n)).
    2. Trường hợp biên (Edge Cases): Xử lý kỹ các giá trị null, mảng rỗng, giá trị giới hạn cực đại/cực tiểu của kiểu dữ liệu.
    3. Mã nguồn mẫu: Triển khai giải pháp rõ ràng, súc tích bằng các cấu trúc dữ liệu cơ bản, tránh lạm dụng bộ nhớ phụ khi không cần thiết.
    12

    Làm thế nào để triển khai và tối ưu hóa thuật toán hoặc cấu trúc dữ liệu liên quan đến HashMap implementation trong chủ đề Heaps and Maps?

    Middle

    Trong lập trình giải thuật với Heaps and Maps, việc làm chủ HashMap implementation yêu cầu lập trình viên hiểu rõ cấu trúc vật lý trong bộ nhớ và độ phức tạp tính toán:

    1. Độ phức tạp: Luôn đánh giá Time Complexity (thời gian) và Space Complexity (không gian) tối ưu nhất (ví dụ: tối ưu từ O(n^2) xuống O(n log n)).
    2. Trường hợp biên (Edge Cases): Xử lý kỹ các giá trị null, mảng rỗng, giá trị giới hạn cực đại/cực tiểu của kiểu dữ liệu.
    3. Mã nguồn mẫu: Triển khai giải pháp rõ ràng, súc tích bằng các cấu trúc dữ liệu cơ bản, tránh lạm dụng bộ nhớ phụ khi không cần thiết.
    13

    Làm thế nào để triển khai và tối ưu hóa thuật toán hoặc cấu trúc dữ liệu liên quan đến Stack memory allocations trong chủ đề Stacks?

    Middle

    Trong lập trình giải thuật với Stacks, việc làm chủ Stack memory allocations yêu cầu lập trình viên hiểu rõ cấu trúc vật lý trong bộ nhớ và độ phức tạp tính toán:

    1. Độ phức tạp: Luôn đánh giá Time Complexity (thời gian) và Space Complexity (không gian) tối ưu nhất (ví dụ: tối ưu từ O(n^2) xuống O(n log n)).
    2. Trường hợp biên (Edge Cases): Xử lý kỹ các giá trị null, mảng rỗng, giá trị giới hạn cực đại/cực tiểu của kiểu dữ liệu.
    3. Mã nguồn mẫu: Triển khai giải pháp rõ ràng, súc tích bằng các cấu trúc dữ liệu cơ bản, tránh lạm dụng bộ nhớ phụ khi không cần thiết.
    14

    Làm thế nào để triển khai và tối ưu hóa thuật toán hoặc cấu trúc dữ liệu liên quan đến Knapsack Problem trong chủ đề Dynamic Programming?

    Middle

    Trong lập trình giải thuật với Dynamic Programming, việc làm chủ Knapsack Problem yêu cầu lập trình viên hiểu rõ cấu trúc vật lý trong bộ nhớ và độ phức tạp tính toán:

    1. Độ phức tạp: Luôn đánh giá Time Complexity (thời gian) và Space Complexity (không gian) tối ưu nhất (ví dụ: tối ưu từ O(n^2) xuống O(n log n)).
    2. Trường hợp biên (Edge Cases): Xử lý kỹ các giá trị null, mảng rỗng, giá trị giới hạn cực đại/cực tiểu của kiểu dữ liệu.
    3. Mã nguồn mẫu: Triển khai giải pháp rõ ràng, súc tích bằng các cấu trúc dữ liệu cơ bản, tránh lạm dụng bộ nhớ phụ khi không cần thiết.
    15

    Làm thế nào để triển khai và tối ưu hóa thuật toán hoặc cấu trúc dữ liệu liên quan đến Counting set bits trong chủ đề Bit Manipulation?

    Middle

    Trong lập trình giải thuật với Bit Manipulation, việc làm chủ Counting set bits yêu cầu lập trình viên hiểu rõ cấu trúc vật lý trong bộ nhớ và độ phức tạp tính toán:

    1. Độ phức tạp: Luôn đánh giá Time Complexity (thời gian) và Space Complexity (không gian) tối ưu nhất (ví dụ: tối ưu từ O(n^2) xuống O(n log n)).
    2. Trường hợp biên (Edge Cases): Xử lý kỹ các giá trị null, mảng rỗng, giá trị giới hạn cực đại/cực tiểu của kiểu dữ liệu.
    3. Mã nguồn mẫu: Triển khai giải pháp rõ ràng, súc tích bằng các cấu trúc dữ liệu cơ bản, tránh lạm dụng bộ nhớ phụ khi không cần thiết.

    vừa nâng cấp PRO khóa 1 phút trước   Tìm hiểu khóa học