Cấu trúc Dữ liệu là nền tảng vững chắc cho mọi lập trình viên. Việc nắm vững các khái niệm này không chỉ giúp bạn viết ra những đoạn code hiệu quả mà còn là "chìa khóa vàng" để vượt qua những vòng phỏng vấn kỹ thuật đầy thử thách. Bài viết này, VietnamWorks inTECH sẽ giới thiệu những câu hỏi phỏng vấn phổ biến liên quan đến cấu trúc dữ liệu, từ những khái niệm cơ bản cho đến những phần "thách đố" phức tạp.

II. Câu hỏi phỏng vấn về cấu trúc dữ liệu cơ bản dành cho người mới bắt đầu

1. Cấu trúc dữ liệu (Data Structure) là gì?

Cấu trúc dữ liệu (Data Structure) là cách tổ chức, lưu trữ và quản lý dữ liệu trong bộ nhớ máy tính để có thể truy xuất và xử lý dữ liệu một cách hiệu quả. Mỗi cấu trúc dữ liệu được thiết kế để phục vụ cho những mục đích cụ thể, giúp tối ưu hóa thời gian và tài nguyên khi thao tác với dữ liệu, như chèn, xóa, tìm kiếm, và sắp xếp.

2. Mô tả các loại Cấu trúc dữ liệu?

Sau đây là các loại cấu trúc dữ liệu:

  • Danh sách: Một tập hợp các mục liên quan được liên kết tới các mục dữ liệu trước đó và/hoặc sau đó.

  • Mảng: Một tập hợp các giá trị giống nhau.

  • Bản ghi: Một tập hợp các trường, mỗi trường chứa dữ liệu từ một kiểu dữ liệu duy nhất.

  • Cây: Một cấu trúc dữ liệu tổ chức dữ liệu theo khuôn khổ phân cấp. Dạng cấu trúc dữ liệu này tuân theo thứ tự chèn, xóa và sửa đổi mục dữ liệu.

  • Bảng: Dữ liệu được lưu dưới dạng hàng và cột. Chúng tương tự như bản ghi ở chỗ kết quả hoặc sự thay đổi của dữ liệu được phản ánh trên toàn bộ bảng.

3. Cấu trúc dữ liệu tuyến tính là gì? Hãy nêu một vài ví dụ.

Cấu trúc dữ liệu tuyến tính là cấu trúc trong đó các phần tử được sắp xếp theo thứ tự liên tiếp, mỗi phần tử chỉ có một phần tử kề trước và một phần tử kề sau (trừ phần tử đầu và phần tử cuối).

Ví dụ về cấu trúc dữ liệu tuyến tính là Mảng (Array), Ngăn xếp (Stack), Chuỗi, Hàng đợi (Queue) và Danh sách liên kết (Linked List).

4. Một số ứng dụng của Cấu trúc dữ liệu là gì? (đây là một trong những câu hỏi thường gặp nhất)

 Cấu trúc dữ liệu được ứng dụng trong nhiều lĩnh vực: Phân tích số, hệ điều hành, AI, thiết kế trình biên dịch, quản lý cơ sở dữ liệu, đồ họa, phân tích thống kê và mô phỏng, . . .

5. Sự khác biệt giữa cấu trúc tập tin và cấu trúc lưu trữ là gì?

Sự khác biệt nằm ở vùng bộ nhớ được truy cập. Cấu trúc tập tin tập trung vào cách tổ chức và truy cập dữ liệu từ góc độ logic (người dùng và ứng dụng), trong khi cấu trúc lưu trữ làm việc ở cấp độ vật lý với các thiết bị lưu trữ nhằm quản lý và lưu trữ dữ liệu một cách tối ưu.

6. Mảng đa chiều là gì?

Mảng đa chiều là một loại cấu trúc dữ liệu trong lập trình, trong đó một mảng có thể chứa nhiều mảng khác, tạo thành một cấu trúc mảng nhiều cấp. Các phần tử trong mảng đa chiều được tổ chức theo dạng lưới hoặc khối, và có thể được truy cập bằng nhiều chỉ số.

7. Các phần tử của mảng 2D được lưu trữ trong bộ nhớ như thế nào?

Các phần tử của mảng 2D thực chất được lưu trữ trong bộ nhớ như một mảng một chiều liên tục. Điều này có nghĩa là dù chúng ta xem mảng 2D như một ma trận với các hàng và cột, bộ nhớ máy tính sẽ lưu trữ các phần tử này theo cách tuyến tính, với một trong hai cách tiếp cận chính: lưu trữ theo hàng (row-major order) hoặc lưu trữ theo cột (column-major order).

  •  Lưu trữ theo hàng: Các phần tử của mảng được lưu trữ theo thứ tự từng hàng, mỗi hàng liên tiếp với hàng trước đó.

  • Lưu trữ theo cột: Các phần tử của mảng được lưu trữ theo thứ tự từng cột, mỗi cột liên tiếp với cột trước đó.

8. Cấu trúc dữ liệu danh sách liên kết là gì?

Đây là một trong những câu hỏi phỏng vấn về cấu trúc dữ liệu thường gặp nhất mà người phỏng vấn mong đợi bạn đưa ra câu trả lời chi tiết. Hãy cố gắng giải thích càng nhiều càng tốt thay vì kết thúc câu trả lời của bạn trong một câu! Bạn có thể tham khảo câu trả lời bên dưới:

Đây là một Cấu trúc Dữ liệu tuyến tính hoặc một chuỗi các đối tượng dữ liệu, trong đó các phần tử không được lưu trữ ở các vị trí bộ nhớ liền kề. Các phần tử được liên kết với nhau bằng cách sử dụng các con trỏ để tạo thành một chuỗi. Mỗi phần tử là một đối tượng riêng lẻ, được gọi là node. Mỗi node có hai thành phần: một trường dữ liệu và một tham chiếu đến node kế tiếp. Điểm bắt đầu của một danh sách liên kết được gọi là head. Khi danh sách rỗng, head là một tham chiếu rỗng, và node cuối cùng sẽ tham chiếu đến null.

Danh sách liên kết là một cấu trúc dữ liệu động, trong đó số lượng các node không cố định, và danh sách có khả năng tăng hoặc giảm kích thước theo nhu cầu.

Nó được áp dụng trong những trường hợp sau:

  • Chúng ta phải xử lý với một số lượng đối tượng không xác định hoặc không biết có bao nhiêu phần tử trong danh sách.

  • Chúng ta cần các thao tác thêm/xóa phần tử có thời gian thực hiện O(1) (thời gian hằng số), như trong các hệ thống tính toán thời gian thực, nơi mà tính dự đoán về thời gian là rất quan trọng.

  • Việc truy cập ngẫu nhiên đến bất kỳ phần tử nào trong danh sách không cần thiết.

  • Thuật toán yêu cầu một cấu trúc dữ liệu có thể lưu trữ các đối tượng bất kể địa chỉ vật lý của chúng trong bộ nhớ.

  • Chúng ta cần chèn các phần tử vào giữa danh sách, như trong trường hợp hàng đợi ưu tiên (priority queue).

9. Danh sách liên kết (linked list) được xem là cấu trúc dữ liệu tuyến tính hay phi tuyến? 

Danh sách liên kết (linked list) được xem là cấu trúc dữ liệu tuyến tính. Mặc dù mỗi phần tử của danh sách liên kết không nằm liền kề nhau trong bộ nhớ (khác với mảng), nhưng chúng vẫn được liên kết theo thứ tự tuyến tính nhờ các con trỏ. Điều này có nghĩa là khi duyệt qua danh sách liên kết, ta phải tuân theo một thứ tự nhất định, bắt đầu từ phần tử đầu tiên và đi đến phần tử cuối cùng thông qua các liên kết (con trỏ).

Do đó, xét về cách truy cập dữ liệu theo thứ tự, danh sách liên kết vẫn là một cấu trúc dữ liệu tuyến tính.

10. Ưu điểm của danh sách liên kết (Linked List) so với mảng (Array)? Khi nào chúng ta sử dụng Linked List và khi nào dùng Array? 

Đây là một câu hỏi phổ biến trong phỏng vấn về cấu trúc dữ liệu! Những ưu điểm của danh sách liên kết (Linked List) so với mảng (Array) bao gồm:

  • Chèn và xóa: Việc chèn và xóa các node trong danh sách liên kết dễ dàng hơn, vì chỉ cần cập nhật địa chỉ trong con trỏ của các node liền kề. Trong mảng, việc này đòi hỏi phải tạo chỗ trống cho các phần tử mới và dịch chuyển các phần tử hiện có, tốn nhiều chi phí hơn.

  • Cấu trúc dữ liệu động: Danh sách liên kết là một cấu trúc dữ liệu động, không cần kích thước cố định ban đầu vì nó có thể tăng hoặc giảm khi chương trình chạy bằng cách cấp phát hoặc giải phóng bộ nhớ. Trong khi đó, mảng có kích thước cố định và phải được lưu trữ trước trong bộ nhớ chính.

  • Không lãng phí bộ nhớ: Vì kích thước của danh sách liên kết có thể tăng hoặc giảm dựa trên nhu cầu của chương trình, và bộ nhớ chỉ được cấp phát khi cần thiết, không có sự lãng phí bộ nhớ. Trong mảng, nếu khai báo một mảng kích thước 10 và chỉ lưu trữ 5 phần tử, 5 ô nhớ còn lại sẽ bị lãng phí.

  • Triển khai: Các cấu trúc dữ liệu như ngăn xếp (stack) và hàng đợi (queue) dễ dàng triển khai bằng danh sách liên kết hơn so với mảng.

Một số tình huống sử dụng danh sách liên kết thay vì mảng:

  • Khi chúng ta không biết trước giới hạn số lượng phần tử.

  • Khi cần thực hiện nhiều thao tác thêm hoặc xóa phần tử.

  • Khi không cần truy cập ngẫu nhiên đến các phần tử.

  • Khi cần chèn các phần tử vào giữa danh sách, như trong hàng đợi ưu tiên.

Một số tình huống sử dụng mảng thay vì danh sách liên kết:

  • Khi cần truy cập ngẫu nhiên hoặc đánh chỉ mục cho các phần tử.

  • Khi biết trước số lượng phần tử để cấp phát chính xác bộ nhớ.

  • Khi cần tốc độ cao để duyệt qua tất cả các phần tử.

  • Khi quan tâm đến bộ nhớ; mảng chiếm ít bộ nhớ hơn danh sách liên kết vì mỗi phần tử trong mảng chỉ là dữ liệu, trong khi mỗi node trong danh sách liên kết cần thêm con trỏ để liên kết với các phần tử khác.

Tóm lại, việc lựa chọn sử dụng danh sách liên kết hay mảng phụ thuộc vào yêu cầu về không gian, thời gian, và độ phức tạp của việc triển khai.

11. Danh sách liên kết kép (doubly-linked list) là gì? Nêu ví dụ. 

Đây là một dạng phức tạp của danh sách liên kết, trong đó mỗi node có hai liên kết, một liên kết tới node tiếp theo và một liên kết tới node trước đó. Điều này cho phép duyệt qua các phần tử dữ liệu theo cả hai chiều.

Ví dụ:

  • Danh sách phát nhạc với các nút điều hướng "tiếp theo" và "trước đó".

  • Bộ nhớ cache của trình duyệt với các trang đã truy cập trước đó và tiếp theo.

  • Chức năng hoàn tác (undo) và làm lại (redo) trong trình duyệt, nơi bạn có thể quay lại trang trước đó.

12. Làm thế nào để tham chiếu tất cả các phần tử trong một mảng một chiều? 

Sử dụng một vòng lặp có chỉ số (indexed loop), chúng ta có thể truy cập tất cả các phần tử trong mảng một chiều. Bộ đếm bắt đầu từ 0 đến kích thước tối đa của mảng (n) trừ 1. Chỉ số của vòng lặp được dùng làm chỉ số mảng để lần lượt tham chiếu đến tất cả các phần tử.

13. Cấu trúc dữ liệu động (dynamic Data Structures) là gì? Đưa ra một vài ví dụ. 

Đây là các tập hợp dữ liệu trong bộ nhớ có thể mở rộng và co lại tùy thuộc vào việc chương trình chạy. Điều này cho phép lập trình viên kiểm soát chính xác lượng bộ nhớ được sử dụng.

Ví dụ: mảng động (dynamic array), danh sách liên kết (linked list), ngăn xếp (stack), hàng đợi (queue), và heap.

14. Thuật toán (algorithm) là gì?

Thuật toán là một phương pháp từng bước để giải quyết vấn đề hoặc xử lý dữ liệu. Nó định nghĩa một tập hợp các hướng dẫn cần thực hiện theo thứ tự nhất định để đạt được kết quả mong muốn.

15. Tại sao chúng ta cần phân tích thuật toán?
Một vấn đề có thể được giải quyết theo nhiều cách khác nhau bằng các thuật toán khác nhau. Việc Phân tích cung cấp sự ước tính về các tài nguyên cần thiết để thực hiện một thuật toán nhằm giải quyết một vấn đề tính toán cụ thể. Phân tích này xác định thời gian và không gian cần thiết để thực hiện thuật toán.

  • Độ phức tạp thời gian (Time complexity) của một thuật toán đo lường thời gian thực hiện thuật toán theo hàm số chiều dài của đầu vào.

  • Độ phức tạp không gian (Space complexity) đo lường lượng bộ nhớ mà một thuật toán cần sử dụng để chạy theo hàm số chiều dài của đầu vào.

16. Ngăn xếp (Stack) là gì?
Ngăn xếp là một kiểu dữ liệu trừu tượng mô tả một cấu trúc dữ liệu tuyến tính, giống như một chồng đồ vật vật lý, nơi bạn chỉ có thể lấy ra phần tử ở trên cùng. Do đó, các thao tác chèn (push) và xóa (pop) phần tử chỉ thực hiện tại một đầu gọi là đỉnh (top) của ngăn xếp, theo thứ tự LIFO (Last In First Out - Vào sau ra trước) hoặc FILO (First In Last Out - Vào trước ra sau).

17. Ngăn xếp được sử dụng như thế nào?

Ngăn xếp được sử dụng trong các trường hợp:

  • Biểu thức, đánh giá hoặc chuyển đổi các biểu thức tiền tố, hậu tố, và trung tố.

  • Phân tích cú pháp.

  • Đảo ngược chuỗi ký tự.

  • Kiểm tra dấu ngoặc.

  • Quay lui (Backtracking).

18. Những thao tác nào có thể thực hiện trên ngăn xếp?

Ngăn xếp thực hiện các thao tác dựa trên nguyên tắc LIFO. Các thao tác cơ bản bao gồm:

  • PUSH: Thêm một phần tử mới vào đỉnh ngăn xếp. Nếu ngăn xếp đầy (TOP = MAX-1), xuất hiện thông báo OVERFLOW.

  • POP: Loại bỏ phần tử ở đỉnh ngăn xếp. Nếu ngăn xếp trống (TOP = NULL), thông báo UNDERFLOW được hiển thị.

  • PEEK: Trả về giá trị của phần tử ở đỉnh ngăn xếp mà không loại bỏ nó.

II. Câu hỏi phỏng vấn về cấu trúc dữ liệu dành cho người có kinh nghiệm

19. Biểu thức hậu tố (postfix expression) là gì?

Biểu thức hậu tố bao gồm các toán tử và toán hạng, với toán tử đứng sau toán hạng. Ví dụ: biểu thức hậu tố chính xác là A B + C *.

20. Cấu trúc dữ liệu hàng đợi (Queue) là gì?

Hàng đợi là một kiểu dữ liệu trừu tượng xác định một cấu trúc dữ liệu tuyến tính, hoạt động theo nguyên tắc FIFO (First In First Out - Vào trước ra trước). Các thao tác chèn thực hiện ở một đầu gọi là REAR, và thao tác xóa thực hiện ở đầu còn lại gọi là FRONT.

21. Một số ứng dụng của hàng đợi (Queue)?

  • Ưu tiên công việc, ví dụ: danh sách chờ cho tài nguyên chung như máy in, CPU, hoặc hệ thống tổng đài.

  • Truyền dữ liệu không đồng bộ, ví dụ: ống dẫn (pipes), IO tệp tin, và sockets.

  • Bộ đệm trong các ứng dụng như trình phát nhạc MP3 hoặc CD.

  • Duy trì danh sách phát trong trình phát media.

22. Dequeue là gì?

Dequeue (Double-Ended Queue) là hàng đợi hai đầu, nơi phần tử có thể được thêm vào hoặc xóa khỏi cả hai đầu (FRONT và REAR).

23. Các thao tác nào có thể thực hiện trên hàng đợi (Queue)?

  • enqueue(): Thêm phần tử vào cuối hàng đợi.

  • dequeue(): Xóa phần tử từ đầu hàng đợi.

  • init(): Khởi tạo hàng đợi.

  • isEmpty(): Kiểm tra xem hàng đợi có trống không.

  • front(): Lấy giá trị của phần tử đầu tiên mà không xóa.

  • rear(): Lấy phần tử cuối cùng của hàng đợi.

24. Những lợi ích của heap so với stack là gì?

Trong các câu hỏi phỏng vấn về cấu trúc dữ liệu, bạn nên nêu ra nhiều lợi ích khác nhau của heap so với stack, kèm theo các ví dụ nếu có thể. Điều này sẽ cho thấy sự hiểu biết chuyên môn của bạn. Thông thường, cả heap và stack đều là thành phần của bộ nhớ và được sử dụng trong Java cho các nhu cầu khác nhau:

  • Heap linh hoạt hơn stack vì không gian bộ nhớ có thể được cấp phát và giải phóng động khi cần thiết.

  • Bộ nhớ heap được dùng để lưu trữ các đối tượng trong Java, trong khi bộ nhớ stack được dùng để lưu trữ các biến cục bộ và các cuộc gọi hàm.

  • Các đối tượng được tạo ra trong heap có thể được nhìn thấy bởi tất cả các luồng, trong khi các biến lưu trữ trong stack chỉ được nhìn thấy bởi chủ sở hữu dưới dạng bộ nhớ riêng.

  • Khi sử dụng đệ quy, kích thước của bộ nhớ heap lớn hơn trong khi bộ nhớ stack dễ bị đầy nhanh hơn.

25. Cấu trúc dữ liệu stack có thể được trong trường hợp nào?

  • Đánh giá biểu thức

  • Quay lui (Backtracking)

  • Quản lý bộ nhớ

  • Gọi và trả về hàm

26. Sự khác biệt giữa PUSH và POP là gì?

Về mặt cấu trúc dữ liệu, đây là một trong những câu hỏi thường gặp nhất.

  • PUSH dùng để thêm một mục vào stack, trong khi POP dùng để loại bỏ một mục.

  • PUSH nhận hai tham số: tên của stack mà dữ liệu sẽ được thêm vào và giá trị của mục cần thêm. POP chỉ cần tên của stack.

  • Khi stack đã đầy và một lệnh PUSH khác được thực hiện, bạn sẽ gặp lỗi tràn stack (stack overflow), nghĩa là stack không còn đủ chỗ cho lệnh PUSH cuối cùng. Trong POP, lỗi thiếu stack (stack underflow) xảy ra khi bạn cố gắng POP một stack đã rỗng.

27. Thuật toán sắp xếp nào được coi là nhanh nhất? Tại sao?

Không có một thuật toán sắp xếp nào là tốt nhất, vì mỗi thuật toán được thiết kế cho một cấu trúc dữ liệu và tập dữ liệu cụ thể. Tuy nhiên, thuật toán QuickSort thường được coi là nhanh nhất vì nó có hiệu suất tốt nhất cho hầu hết các input.

Ưu điểm của nó so với các thuật toán sắp xếp khác bao gồm:

  • Hiệu quả với bộ nhớ cache: QuickSort quét và phân vùng đầu vào theo tuyến tính. Điều này có nghĩa là chúng ta có thể tận dụng tối đa mỗi lần tải cache.

  • Có thể bỏ qua một số phép hoán đổi: Do QuickSort nhạy cảm với input đã được sắp xếp theo thứ tự đúng, nó có thể bỏ qua một số phép hoán đổi.

  • Hiệu quả ngay cả trong các tập dữ liệu tệ nhất, vì thứ tự đầu vào thường là ngẫu nhiên.

  • Dễ dàng thích nghi với input đã được sắp xếp hoặc gần như đã được sắp xếp.

  • Khi tốc độ được ưu tiên hơn độ ổn định.

28. Merge sort là gì? Nó hoạt động như thế nào?

Merge sort là một thuật toán “chia để trị” nhằm sắp xếp dữ liệu. Nó hoạt động bằng cách kết hợp và sắp xếp các dữ liệu kề nhau để tạo ra các danh sách được sắp xếp lớn hơn, sau đó các danh sách đã được sắp xếp này kết hợp đệ quy để tạo ra các danh sách được sắp xếp lớn hơn nữa cho đến khi bạn có 1 danh sách duy nhất cuối cùng. 

29. Selection sort hoạt động như thế nào?

Selection sort hoạt động bằng cách liên tục chọn số nhỏ nhất theo thứ tự tăng dần từ danh sách và đặt nó ở đầu. Quá trình này được lặp lại hướng về phía cuối danh sách hoặc mảng con đã được sắp xếp.

  • Quét tất cả các mục và tìm số nhỏ nhất. Hoán đổi vị trí với mục đầu tiên. Lặp lại selection sort trên các mục còn lại N-1.

  • Thời gian thực thi: trường hợp tốt nhất O(n²); trường hợp tồi tệ nhất O(n²)

  • Độ phức tạp về không gian: trường hợp tồi tệ nhất O(1)

30. Phân tích độ phức tạp (asymptotic analysis) của một thuật toán là gì?

Phân tích độ phức tạp (asymptotic analysis) của một thuật toán là phương pháp xác định và đánh giá hiệu suất của thuật toán, đặc biệt là trong các tình huống khi kích thước đầu vào trở nên rất lớn. Nó giúp chúng ta hiểu được cách mà thời gian thực thi hoặc tài nguyên sử dụng của thuật toán thay đổi khi kích thước đầu vào thay đổi. Việc này giúp lập trình viên và nhà nghiên cứu hiểu rõ hơn về hiệu suất của thuật toán và so sánh các giải pháp khác nhau để chọn phương pháp tối ưu nhất cho các bài toán thực tế.

31. Các ký hiệu độ phức tạp (asymptotic notations) là gì?

Các ký hiệu độ phức tạp (asymptotic notations) là các công cụ dùng để mô tả và phân tích hiệu suất của thuật toán, đặc biệt là khi kích thước đầu vào trở nên rất lớn. Chúng giúp xác định mức độ tăng trưởng của thời gian thực thi hoặc sử dụng bộ nhớ của thuật toán so với kích thước đầu vào. Dưới đây là các ký hiệu chính:

  • Little o Notation (o): Mô tả một hàm tăng trưởng nhỏ hơn nhiều so với một hàm khác khi kích thước đầu vào tăng vô hạn. Ký hiệu: o(f(n))

  • Big Omega Notation (Ω): Mô tả giới hạn dưới của độ phức tạp, tức là số lượng phép toán tối thiểu mà thuật toán cần trong trường hợp tốt nhất. Ký hiệu: Ω(f(n))

  • Big Theta Notation (Θ): Mô tả giới hạn chính xác của độ phức tạp, tức là số lượng phép toán trung bình cần thiết, bao gồm cả giới hạn trên và dưới. Ký hiệu: Θ(f(n))

  • Big O Notation (O): Mô tả giới hạn trên của độ phức tạp, tức là số lượng phép toán tối đa mà thuật toán có thể cần trong trường hợp xấu nhất. Ký hiệu: O(f(n))

32. Một số ví dụ về thuật toán “chia để trị” là gì?

  • QuickSort: Thuật toán sắp xếp. Phương pháp chọn một phần tử pivot và sắp xếp các phần tử mảng sao cho tất cả các mục nhỏ hơn phần tử pivot sẽ đi về phía bên trái của pivot và tất cả các phần tử lớn hơn phần tử pivot sẽ di chuyển về phía bên phải.

  • Merge Sort: Đây cũng là một thuật toán sắp xếp. Thuật toán chia mảng thành hai nửa, sắp xếp chúng đệ quy, và sau đó kết hợp hai nửa đã được sắp xếp. Mục tiêu của các điểm gần nhau là xác định cặp điểm gần nhất trong một tập hợp điểm trên mặt phẳng x-y. Vấn đề có thể được giải quyết trong thời gian O(n²) bằng cách tính khoảng cách giữa mỗi cặp điểm và so sánh chúng để xác định khoảng cách ngắn nhất.

33. Định nghĩa cấu trúc dữ liệu đồ thị (graph Data Structure) là gì?

Cấu trúc dữ liệu đồ thị (graph data structure) là một loại cấu trúc dữ liệu không theo thứ tự, được dùng để mô tả các mối quan hệ giữa các đối tượng. Đồ thị bao gồm các đỉnh (vertices hoặc nodes) và các cạnh (edges hoặc arcs) nối giữa các đỉnh. Cấu trúc dữ liệu đồ thị rất quan trọng trong nhiều lĩnh vực và ứng dụng, từ mạng máy tính đến các bài toán tối ưu hóa và phân tích mạng xã hội.

34. Các ứng dụng của cấu trúc dữ liệu đồ thị là gì?

  • Lưới giao thông, nơi các trạm được đại diện là các đỉnh và các tuyến đường là các cạnh của đồ thị.

  • Đồ thị tiện ích của điện hoặc nước, nơi các đỉnh là các điểm kết nối và các cạnh là dây hoặc ống kết nối chúng.

  • Đồ thị mạng xã hội để xác định luồng thông tin và các điểm nóng (cạnh và đỉnh).

  • Mạng nơ-ron, nơi các đỉnh đại diện cho các nơ-ron và các cạnh là các synapse giữa chúng.

35. Mô tả một số loại cây (tree) trong cấu trúc dữ liệu

  • Cây tổng quát (The General Tree): Cây được gọi là cây tổng quát nếu cấu trúc của nó không bị hạn chế. Trong cây tổng quát, mỗi nút có thể có một số con vô hạn, và tất cả các cây khác là các tập con của cây này.

  • Cây nhị phân (The Binary Tree): Cây nhị phân là loại cây trong đó mỗi nút cha có ít nhất hai con. Các con được gọi là con trái và con phải. Cây này phổ biến hơn so với nhiều cây khác. Khi các giới hạn và đặc điểm cụ thể được đưa ra cho cây nhị phân, nhiều loại cây như AVL, BST (Cây tìm kiếm nhị phân), RBT và các loại khác cũng được sử dụng.

  • Cây tìm kiếm nhị phân (Tree of Binary Search): Cây tìm kiếm nhị phân (BST) là một mở rộng của cây nhị phân bao gồm nhiều ràng buộc tùy chọn. Trong BST, giá trị của con trái của một nút phải nhỏ hơn hoặc bằng giá trị của nút cha, trong khi giá trị của con phải luôn lớn hơn hoặc bằng giá trị của nút cha.

  • Cây AVL: Cây AVL là cây tìm kiếm nhị phân cân bằng tự động. Thuật ngữ AVL được đặt theo tên các nhà sáng chế Adelson-Velskii và Landis. Đây là cây đầu tiên đạt được sự cân bằng động. Mỗi nút trong cây AVL được gán một yếu tố cân bằng dựa trên việc cây có cân bằng hay không. Các nút con có chiều cao tối đa là một cây AVL.

  • Cây Đỏ-Đen (Red-Black Tree): Cây đỏ-đen là một loại cây tự cân bằng khác. Thuật ngữ "đỏ-đen" được lấy từ các thuộc tính của cây đỏ-đen, trong đó mỗi nút có thể được tô màu đỏ hoặc đen. Cây đỏ-đen giúp duy trì sự cân bằng của cấu trúc dữ liệu cây. Mặc dù cây này không hoàn toàn cân bằng, nhưng quá trình tìm kiếm trong cây chỉ mất thời gian O(log n).

  • Cây N-ary (N-ary Tree): Cây mà mỗi nút có tối đa N con. Ví dụ, một cây nhị phân là một cây 2-ary, trong khi một cây bốn nhánh là một cây 4-ary.

37. Sự Khác Biệt Giữa Cây B và Cây B+

Cây B là một cây m-way tự cân bằng, trong đó m xác định bậc của cây. Tùy thuộc vào giá trị của m, cây B là một mở rộng của cây tìm kiếm nhị phân, trong đó một nút có thể chứa nhiều khóa và nhiều hơn hai con. Dữ liệu trong cây B được sắp xếp theo cách mà các giá trị thấp hơn nằm ở cây con trái và các giá trị cao hơn nằm ở cây con phải.

Cây B+ là một loại cây tự cân bằng nâng cao, trong đó mọi đường từ gốc cây đến các lá đều có chiều dài bằng nhau. Điều này có nghĩa là tất cả các nút lá đều nằm ở cùng một cấp độ. Một số nút lá không thể xuất hiện ở cấp độ ba, trong khi các nút khác xuất hiện ở cấp độ hai.

38. Các ưu điểm của tìm kiếm nhị phân so với tìm kiếm tuyến tính

Trong một danh sách đã được sắp xếp:

  • Tìm kiếm nhị phân hiệu quả hơn tìm kiếm tuyến tính vì chúng ta thực hiện ít phép so sánh hơn. Với tìm kiếm tuyến tính, mỗi lần không tìm thấy giá trị cần tìm, chúng ta chỉ có thể loại bỏ một phần tử, nhưng với tìm kiếm nhị phân, chúng ta loại bỏ một nửa tập hợp với mỗi phép so sánh.

  • Tìm kiếm nhị phân chạy trong thời gian O(log n) so với thời gian O(n) của tìm kiếm tuyến tính. Điều này có nghĩa là, càng nhiều phần tử có trong mảng tìm kiếm, tìm kiếm nhị phân càng nhanh hơn so với tìm kiếm tuyến tính.

39. Phân Biệt NULL và VOID

  • Null là một giá trị, trong khi Void là một kiểu dữ liệu định danh.

  • Null chỉ ra giá trị rỗng cho một biến, trong khi void chỉ ra con trỏ không có kích thước ban đầu.

  • Null có nghĩa là nó chưa bao giờ tồn tại; Void có nghĩa là nó đã tồn tại nhưng không có hiệu lực.

41. Cấp phát bộ nhớ động có giúp quản lý dữ liệu không?

Có. Cấp phát bộ nhớ động lưu trữ các kiểu dữ liệu cấu trúc đơn giản tại thời điểm chạy. Nó có khả năng kết hợp các khối cấu trúc được cấp phát riêng biệt để tạo thành các cấu trúc tổ hợp có thể mở rộng và thu nhỏ khi cần thiết, giúp quản lý dữ liệu của các khối dữ liệu với kích thước tùy ý, theo thứ tự tùy ý.

42. Các cách xác định liên kết danh sách có vòng lặp hay không

  • Sử dụng hashing

  • Sử dụng phương pháp các nút đã truy cập (có hoặc không thay đổi cấu trúc dữ liệu liên kết cơ bản)

  • Thuật toán tìm chu trình của Floyd

43. Một số ứng dụng của cấu trúc đa liên kết

  • Ma trận thưa thớt

  • Tạo chỉ mục

44. Mảng răng cưa (Jagged Array) là gì?

Mảng răng cưa (hay còn gọi là Jagged Array) là một kiểu mảng trong đó các phần tử của mảng là các mảng con, và các mảng con này có thể có kích thước khác nhau.

Điều này khác với mảng hai chiều truyền thống (mảng hình chữ nhật), nơi tất cả các hàng và cột đều có kích thước đồng đều. Mảng răng cưa cho phép các hàng có số lượng phần tử khác nhau, vì vậy nó có thể tiết kiệm bộ nhớ và phù hợp với các tình huống mà dữ liệu không đều.

45. Cấu trúc dữ liệu heap tối đa (Max Heap) là gì?

Đây là một loại cấu trúc dữ liệu heap, trong đó giá trị của nút gốc lớn hơn hoặc bằng giá trị của các nút con của nó.

46. Làm thế nào để tìm chiều cao của một nút trong cây?

Chiều cao của một nút bằng số cạnh trên đường dài nhất từ nút đến lá, trong đó độ sâu của nút lá là 0.

Lời kết

Hiểu biết vững về cấu trúc dữ liệu không chỉ giúp giải quyết các bài toán lập trình một cách hiệu quả mà còn chứng tỏ khả năng phân tích và thiết kế hệ thống của bạn. Hy vọng rằng những câu hỏi phỏng vấn mà VietnamWorks inTECH đã chia sẻ sẽ giúp bạn chuẩn bị tốt hơn cho cuộc phỏng vấn sắp tới.

VietnamWorks inTECH

TẠO TÀI KHOẢN MỚI: XEM FULL “1 TÁCH CODEFEE” - NHẬN SLOT TƯ VẤN CV TỪ CHUYÊN GIA - CƠ HỘI RINH VỀ VOUCHER 200K