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.
Hãy giải thích sự khác nhau về cơ chế hoạt động giữa HashMap và HashTable?
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.null. Chậm hơn HashMap. Hiện nay, trong đa luồng người ta khuyên dùng ConcurrentHashMap thay vì HashTable.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)?
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:
- 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).
- 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.
- 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.
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?
Sử dụng kỹ thuật hai con trỏ chạy với tốc độ khác nhau (Fast & Slow Pointers):
- Khởi tạo con trỏ
slowvàfastcùng trỏ vàohead. - 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). - Khi con trỏ
fastđi đến cuối danh sách (fast == nullhoặcfast.next == null), con trỏslowsẽ 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.
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)?
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:
- 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.
- Lỗi tràn số (Integer Overflow): Công thức tính thông thường là
mid = (low + high) / 2. Nếulowvàhighđề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. - 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ệuhigh - lowluôn là số dương nhỏ hơn giới hạn, và khi cộng vàolowcũ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).
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?
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:
- Khởi tạo một stack rỗng.
- Duyệt qua từng ký tự của chuỗi biểu thức từ trái sang phải.
- Nếu gặp dấu ngoặc mở (
(,[,{), ta đẩy (push) nó vào stack. - 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.
- 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.
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ụ?
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.
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?
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:
- Divide (Chia): Chia bài toán lớn thành các bài toán con nhỏ hơn.
- 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.
- 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:
- 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).
- 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.
- 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.
Sự khác biệt giữa Integration Testing (Kiểm thử tích hợp) và Unit Testing (Kiểm thử đơn vị)? Tại sao cần Mocking trong Unit Test?
Middleware hoạt động như thế nào trong Request Pipeline của một ứng dụng web? Cho ví dụ về xử lý Authentication và CORS?
Middleware hoạt động theo mô hình Onion (củ hành) hoặc Pipeline (đường ống). Khi HTTP Request gửi đến, nó đi qua một chuỗi các middleware được cấu hình sẵn trước khi chạm tới Controller xử lý chính. Response sau đó cũng đi ngược lại qua chuỗi middleware này để trả về cho client.
- Ví dụ xử lý Authentication: Middleware kiểm tra xem Request có chứa JWT Token hợp lệ ở Header hay không. Nếu có, nó giải mã lấy thông tin người dùng gắn vào Request và gọi
next()để đi tiếp. Nếu không, nó chặn Request và trả về mã lỗi 401 Unauthorized ngay lập tức. - Ví dụ xử lý CORS (Cross-Origin Resource Sharing): Middleware chặn các request Preflight (OPTIONS) hoặc các request thông thường để chèn thêm các header cần thiết (như
Access-Control-Allow-Origin,Access-Control-Allow-Methods) vào response nhằm cho phép trình duyệt ở domain khác truy cập tài nguyên một cách hợp lệ.
Mạng phân phối nội dung (CDN) hoạt động trên nguyên lý nào? Giải thích sự khác biệt giữa cơ chế Push CDN và Pull CDN?
CDN hoạt động trên nguyên lý phân tán các máy chủ bộ nhớ đệm (Edge Servers) tại nhiều điểm địa lý khác nhau trên thế giới để lưu trữ bản sao dữ liệu tĩnh (ảnh, video, JS/CSS), giúp giảm độ trễ (latency) khi người dùng truy cập từ khoảng cách xa.
- Push CDN: Lập trình viên chủ động tải (upload/push) nội dung mới lên máy chủ CDN mỗi khi có sự thay đổi. CDN là nơi lưu trữ gốc của các file này.
- Ưu điểm: Tiết kiệm băng thông cho server gốc (Origin Server) vì CDN không cần hỏi lại server gốc để lấy file.
- Nhược điểm: Phải tự quản lý luồng upload và cập nhật khi file thay đổi.
- Pull CDN: Khi người dùng request một file, Edge Server của CDN sẽ kiểm tra xem đã có file đó trong cache chưa. Nếu chưa (Cache Miss), nó sẽ tự động gửi request về server gốc để kéo (pull) file đó về, lưu vào cache tại Edge Server và trả về cho người dùng.
- Ưu điểm: Cực kỳ đơn giản khi tích hợp, CDN tự động đồng bộ hóa với server gốc.
- Nhược điểm: Request đầu tiên của người dùng sẽ bị chậm (do cache miss phải pull từ origin).
Sự khác biệt giữa Entity và Value Object trong Domain-Driven Design (DDD) là gì? Cho ví dụ minh họa?
- Là đối tượng được định danh bằng một ID duy nhất và cố định trong suốt vòng đời của nó. Cho dù các thuộc tính khác thay đổi, Entity đó vẫn là duy nhất.
- Ví dụ:
Customer(Học viên) có ID là 1. Học viên này có thể đổi tên, đổi email nhưng ID vẫn là 1 và hệ thống vẫn xác định đó là cùng một người.
- Là đối tượng không có ID riêng và được định nghĩa hoàn toàn bởi giá trị của các thuộc tính mà nó chứa. Nếu hai Value Objects có tất cả các thuộc tính giống hệt nhau, chúng được coi là tương đương.
- Value Object là bất biến (immutable). Muốn thay đổi, ta bắt buộc phải tạo ra một thực thể Value Object mới thay thế hoàn toàn.
- Ví dụ:
Address(Địa chỉ gồm số nhà, đường, thành phố) hoặcMoney(gồm số tiền và đơn vị tệ). Nếu đổi địa chỉ giao hàng, ta gán một địa chỉ hoàn toàn mới thay vì sửa đổi từng thuộc tính của địa chỉ cũ.
Hãy so sánh các thuật toán cân bằng tải phổ biến: Round Robin, Weighted Round Robin, Least Connections và IP Hash?
Những lỗi phổ biến nào lập trình viên thường mắc phải khi sử dụng Deferred Execution properties trong LINQ?
Trong phát triển dự án với LINQ, việc tối ưu hóa và quản lý tốt Deferred Execution properties là vô cùng quan trọng để đảm bảo tính ổn định và hiệu năng của hệ thống. Dưới đây là các khía cạnh kỹ thuật cốt lõi:
- Cơ chế hoạt động: Nắm vững vòng đời và cách thức hoạt động của
Deferred Execution propertiesdưới runtime để tránh các lỗi logic phổ biến. - Best Practices: Áp dụng các mẫu thiết kế chuẩn, hạn chế tối đa tài nguyên dư thừa và sử dụng các thư viện hỗ trợ tối ưu.
- Khắc phục lỗi: Sử dụng công cụ giám sát (APM, Profiling, Logs) để theo dõi hành vi, phát hiện kịp thời các hiện tượng như rò rỉ bộ nhớ hoặc nghẽn luồng.
- Tối ưu hóa: Cấu hình các tham số hệ thống phù hợp với quy mô tải thực tế của ứng dụng.
Những lỗi phổ biến nào lập trình viên thường mắc phải khi sử dụng GAC (Global Assembly Cache) trong .Net?
Trong phát triển dự án với .Net, việc tối ưu hóa và quản lý tốt GAC (Global Assembly Cache) là vô cùng quan trọng để đảm bảo tính ổn định và hiệu năng của hệ thống. Dưới đây là các khía cạnh kỹ thuật cốt lõi:
- Cơ chế hoạt động: Nắm vững vòng đời và cách thức hoạt động của
GAC (Global Assembly Cache)dưới runtime để tránh các lỗi logic phổ biến. - Best Practices: Áp dụng các mẫu thiết kế chuẩn, hạn chế tối đa tài nguyên dư thừa và sử dụng các thư viện hỗ trợ tối ưu.
- Khắc phục lỗi: Sử dụng công cụ giám sát (APM, Profiling, Logs) để theo dõi hành vi, phát hiện kịp thời các hiện tượng như rò rỉ bộ nhớ hoặc nghẽn luồng.
- Tối ưu hóa: Cấu hình các tham số hệ thống phù hợp với quy mô tải thực tế của ứng dụng.
Những lỗi phổ biến nào lập trình viên thường mắc phải khi sử dụng Web View wrapper trong Ionic?
Trong phát triển dự án với Ionic, việc tối ưu hóa và quản lý tốt Web View wrapper là vô cùng quan trọng để đảm bảo tính ổn định và hiệu năng của hệ thống. Dưới đây là các khía cạnh kỹ thuật cốt lõi:
- Cơ chế hoạt động: Nắm vững vòng đời và cách thức hoạt động của
Web View wrapperdưới runtime để tránh các lỗi logic phổ biến. - Best Practices: Áp dụng các mẫu thiết kế chuẩn, hạn chế tối đa tài nguyên dư thừa và sử dụng các thư viện hỗ trợ tối ưu.
- Khắc phục lỗi: Sử dụng công cụ giám sát (APM, Profiling, Logs) để theo dõi hành vi, phát hiện kịp thời các hiện tượng như rò rỉ bộ nhớ hoặc nghẽn luồng.
- Tối ưu hóa: Cấu hình các tham số hệ thống phù hợp với quy mô tải thực tế của ứng dụng.




