Tổng quan về giáo trình

Tài liệu học tập "Chapter 4: Lists" là một phần trong hệ thống bài giảng học phần Cấu trúc dữ liệu và Giải thuật (Data Structures and Algorithms - DSA), do ThS. Lương Thế Nhân và TS. Trần Giang Sơn biên soạn, thuộc chương trình đào tạo của Khoa Khoa học và Kỹ thuật Máy tính, Trường Đại học Bách khoa – Đại học Quốc gia TP.HCM (Faculty of Computer Science and Engineering, University of Technology, VNU-HCM). Môn học này giữ vị trí cốt lõi trong khối kiến thức cơ sở ngành của các chương trình đào tạo kỹ sư và cử nhân thuộc khối ngành Công nghệ Thông tin, Khoa học Máy tính và Kỹ thuật Phần mềm.

Mục tiêu học tập của tài liệu được thiết lập bám sát hệ thống Chuẩn đầu ra (Course Learning Outcomes) của học phần:

  • L.1: Mô tả các khái niệm danh sách mảng (array list), danh sách liên kết (linked list gồm liên kết đơn, liên kết đôi, đa liên kết), ngăn xếp (stack), hàng đợi (queue) và hàng đợi vòng (circular queue).
  • L.2: Đặc tả cấu trúc lưu trữ của các cấu trúc dữ liệu trên thông qua mã giả (pseudocode).
  • L.3: Liệt kê và mô tả các phương thức xử lý cơ bản bằng mã giả; hiện thực cấu trúc danh sách, stack, queue bằng ngôn ngữ C/C++.
  • L.4: Xây dựng thuật toán đệ quy cho các thao tác trên danh sách, cây, heap, tìm kiếm và đồ thị.
  • L.5: Ứng dụng danh sách, stack, queue vào giải quyết các bài toán thực tế và lựa chọn dạng hiện thực phù hợp (mảng so với danh sách liên kết).
  • L.6: Phân tích độ phức tạp thuật toán sử dụng ký pháp Big-O ($O$) và xây dựng chương trình thực nghiệm để đánh giá hiệu năng của các phương thức.

Cấu trúc bài giảng tiếp cận tuần tự theo quy trình kỹ thuật phần mềm: từ mô hình trừu tượng hóa dữ liệu (Abstract Data Type - ADT), phân loại danh sách, thiết kế cấu trúc lưu trữ bộ nhớ, cài đặt chi tiết trên C++ đến phân tích hiệu năng thuật toán.


Nội dung kiến thức cốt lõi

Các chương/chủ đề chính

Nội dung bài giảng được tổ chức thành 5 phần chuyên đề chính:

  1. Linear list concepts (Khái niệm danh sách tuyến tính): Định nghĩa danh sách tuyến tính là cấu trúc dữ liệu trong đó mỗi phần tử đều có một phần tử kế tiếp duy nhất (unique successor). Phân loại danh sách thành hai nhóm lớn:

    • General list: Không bị giới hạn về phép toán hay vị trí chèn/xóa; bao gồm Unordered list (danh sách ngẫu nhiên, không theo thứ tự) và Ordered list (danh sách có thứ tự sắp xếp dựa trên khóa key).
    • Restricted list: Giới hạn vị trí thao tác tại các đầu mút danh sách, tiêu biểu là Ngăn xếp (Stack - cơ chế LIFO) và Hàng đợi (Queue - cơ chế FIFO).
    • List ADT: Đặc tả kiểu dữ liệu trừu tượng với các thao tác cơ bản (Construct, Insert, Remove, Search, Retrieve, Traverse) và thao tác mở rộng (IsEmpty, IsFull, Size, Clear, Replace, Merge, Append).
  2. Array implementation (Hiện thực danh sách bằng mảng): Nghiên cứu hai kỹ thuật quản lý vùng nhớ liên tục:

    • Automatically Allocated Array: Mảng tĩnh có kích thước tối đa định trước (maxsize) và biến đếm count.
    • Dynamically Allocated Array: Mảng động cấp phát trên vùng nhớ Heap (int* storage), tự động điều chỉnh dung lượng (capacity) và quản lý kích thước thực tế (size).
  3. Singly linked list (Danh sách liên kết đơn): Mô hình tập hợp phần tử có thứ tự, trong đó mỗi phần tử (node) chứa giá trị dữ liệu (data) và địa chỉ trỏ tới phần tử kế tiếp (next/link). Cấu trúc danh sách quản lý thông qua con trỏ đầu danh sách (head) và số lượng phần tử (count).

  4. Other linked lists (Các dạng danh sách liên kết khác): Mở rộng sang danh sách liên kết đôi (doubly linked list), danh sách liên kết vòng (circular linked list) và danh sách đa liên kết (multiple links).

  5. Comparison of implementations of list (So sánh các mô hình hiện thực): Đối chiếu ưu và nhược điểm giữa cấu trúc mảng và danh sách liên kết về chi phí bộ nhớ và thời gian thực thi.

Kiến thức nền tảng được xây dựng

  • Nguyên lý trừu tượng hóa dữ liệu (Data Abstraction): Tách biệt giữa giao diện đặc tả (interface/ADT) và cơ chế cài đặt nội bộ (implementation details). Điều kiện thành công của thao tác: phép chèn (Insertion) thành công khi danh sách chưa đầy, phép xóa/truy xuất (Removal/Retrieval) thành công khi danh sách không rỗng.
  • Mô hình tổ chức bộ nhớ:
    • Bộ nhớ liền kề (Contiguous Allocation): Các phần tử nằm tại các ô nhớ liên tiếp nhau, tính toán địa chỉ phần tử trực tiếp qua chỉ số (index).
    • Bộ nhớ liên kết (Chained Allocation): Các node phân tán tại các vùng nhớ khác nhau, duy trì quan hệ thứ tự thông qua biến con trỏ.
  • Lý thuyết độ phức tạp tính toán (Computational Complexity): Đánh giá thời gian chạy dựa trên cấu trúc điều khiển (tuần tự, rẽ nhánh, lặp) bằng ký pháp Big-O:
    • Cài đặt bằng mảng liên tục: Thao tác Clear, Empty, Full, Size, Replace, Retrieve có độ phức tạp thời gian hằng số $O(1)$. Thao tác Insert và Remove có độ phức tạp $O(n)$ do đòi hỏi dịch chuyển vật lý dữ liệu trong bộ nhớ.

Kỹ năng phát triển

  • Kỹ năng lập trình hệ thống và quản trị bộ nhớ C++:
    • Cài đặt lớp DynamicArray với cơ chế quản lý bộ nhớ thủ công: khởi tạo mảng động bằng toán tử new, giải phóng bộ nhớ bằng delete[] storage, kiểm tra biên rangeCheck với ngoại lệ throw "Index out of bounds!".
    • Kỹ thuật cấp phát mở rộng dung lượng ensureCapacity theo công thức: $$\text{newCapacity} = \frac{\text{capacity} \times 3}{2} + 1$$
    • Tối ưu hóa việc sao chép và dịch chuyển vùng nhớ bằng các hàm cấp thấp memcpy và memmove.
  • Kỹ năng lập trình tổng quát (Generic Programming): Sử dụng C++ Template (template <class ItemType>, template <class List_ItemType>) để xây dựng cấu trúc Node và lớp LinkedList, cho phép cấu trúc dữ liệu lưu trữ đa dạng các kiểu dữ liệu khác nhau.
  • Kỹ năng thiết kế giải thuật danh sách: Xây dựng thuật toán định vị con trỏ (pPre, p), cập nhật liên kết trong các trường hợp chèn node vào danh sách liên kết rỗng, chèn ở đầu (beginning), chèn ở giữa (middle) và chèn ở cuối (end).

Phương pháp giảng dạy và học tập

Phương pháp Nội dung chi tiết
Tiếp cận sư phạm Tuân thủ tiến trình sư phạm 4 bước: Đặc tả lý thuyết ADT $\rightarrow$ Biểu diễn mã giả $\rightarrow$ Hiện thực hóa bằng mã C++ $\rightarrow$ Phân tích độ phức tạp Big-O.
Thực hành mã nguồn Sinh viên trực tiếp xây dựng và biên dịch mã nguồn lớp DynamicArray và LinkedList với các hàm thành phần: insertAt, removeAt, get, set, setCapacity, rangeCheck.
Bài toán ứng dụng Triển khai mô hình danh sách liên kết để giải quyết bài toán biểu diễn đa thức đại số (Polynomial representation), trong đó mỗi node lưu trữ hệ số và số mũ.
Phương pháp đánh giá Đánh giá qua bài tập lập trình C/C++, kiểm tra khả năng phân tích chi phí thuật toán, và bài toán tối ưu hóa lựa chọn cấu trúc lưu trữ phù hợp với yêu cầu thực tế.

Trong quá trình tự học, sinh viên cần thực hiện vẽ sơ đồ trạng thái bộ nhớ và con trỏ trước khi viết mã, sau đó kiểm chứng thuật toán bằng cách viết các chương trình thực nghiệm để đo thời gian thực thi của các phương thức.


Điểm nổi bật và cập nhật

  • Độ chính xác kỹ thuật cao trong mã nguồn: Tài liệu cung cấp cài đặt mã nguồn C++ hoàn chỉnh và tối ưu. Điển hình là việc sử dụng memmove thay vì vòng lặp thủ công khi chèn/xóa phần tử trong DynamicArray, hạn chế lỗi chồng lấn vùng nhớ (memory overlap):
    // Trích đoạn thuật toán insertAt trong tài liệu
    void DynamicArray::insertAt(int index, int value) {
        if (index < 0 || index > size) throw "Index out of bounds!";
        ensureCapacity(size + 1);
        int moveCount = size - index;
        if (moveCount != 0)
            memmove(storage + index + 1, storage + index, sizeof(int) * moveCount);
        storage[index] = value;
        size++;
    }
    
  • Áp dụng chuẩn lập trình hiện đại: Cấu trúc dữ liệu danh sách liên kết được chuẩn hóa dưới dạng C++ Template, giúp mã nguồn có tính trừu tượng cao, tái sử dụng được cho nhiều kiểu dữ liệu nguyên thủy hoặc cấu trúc dữ liệu tự định nghĩa.
  • Gắn kết với các cấu trúc dữ liệu nâng cao: Phân loại danh sách tuyến tính được liên kết trực tiếp với các cấu trúc dữ liệu trừu tượng tiếp theo trong chương trình như Stack (ngăn xếp), Queue (hàng đợi), Heap, Tree (cây) và Graph (đồ thị).

Đối tượng sử dụng giáo trình

  • Sinh viên đại học: Sinh viên các học kỳ đầu ngành Khoa học Máy tính, Kỹ thuật Máy tính, Công nghệ Thông tin, Kỹ thuật Phần mềm và Hệ thống Thông tin.
  • Điều kiện tiên quyết (Prerequisites):
    • Nắm vững kiến thức lập trình cơ bản và nâng cao với C/C++ (kiểu dữ liệu, cấu trúc điều khiển tuần tự, rẽ nhánh, vòng lặp).
    • Hiểu rõ cơ chế con trỏ (pointers), tham chiếu (references), cấp phát bộ nhớ động (new/delete).
    • Kiến thức căn bản về lập trình hướng đối tượng (Class, Struct, Constructor, Destructor, Access Modifiers).
  • Giảng viên: Sử dụng tài liệu làm khung bài giảng chuẩn cho các buổi học lý thuyết và định hướng thực hành tại phòng máy.
  • Tài liệu tham khảo chuyên môn: Phục vụ người học tự nghiên cứu nền tảng cấu trúc dữ liệu phục vụ các kỳ thi tuyển dụng kỹ thuật phần mềm hoặc tối ưu hóa mã nguồn.

Câu hỏi thường gặp

1. Giáo trình này phù hợp với ai?

Tài liệu được thiết kế cho sinh viên đại học khối ngành Công nghệ Thông tin - Khoa học Máy tính đang theo học học phần Cấu trúc dữ liệu và Giải thuật, cũng như lập trình viên cần củng cố kiến thức nền tảng về tổ chức bộ nhớ và giải thuật.

2. Cần chuẩn bị kiến thức nền tảng nào trước khi học?

Người học cần hoàn thành học phần Kỹ thuật Lập trình hoặc Nhập môn Lập trình, thành thạo ngôn ngữ C/C++, đặc biệt là kỹ thuật thao tác với biến con trỏ, quản lý bộ nhớ động và cú pháp C++ Template.

3. Sự khác biệt cốt lõi giữa Mảng động (Dynamic Array) và Danh sách liên kết đơn (Singly Linked List) là gì?

  • Mảng động: Lưu trữ trên vùng nhớ liên tục; truy xuất phần tử ngẫu nhiên đạt thời gian hằng số $O(1)$; thao tác chèn/xóa tốn chi phí $O(n)$ do phải dịch chuyển dữ liệu vật lý.
  • Danh sách liên kết: Lưu trữ trên các node phân tán; truy xuất tuần tự qua con trỏ tốn chi phí $O(n)$; thao tác chèn/xóa tại vị trí đã định vị con trỏ đạt chi phí $O(1)$ mà không cần dời chuyển các phần tử khác.

4. Hệ số tăng trưởng dung lượng của mảng động trong tài liệu được tính như thế nào?

Trong phương thức ensureCapacity, khi dung lượng hiện tại không đủ chứa dữ liệu mới, dung lượng mới được tính theo công thức: $$\text{newCapacity} = \frac{\text{capacity} \times 3}{2} + 1$$ tương đương mức tăng trưởng xấp xỉ 1.5 lần, giúp giảm thiểu tần suất cấp phát lại bộ nhớ và sao chép dữ liệu.

5. Có những cấu trúc dữ liệu nào được phát triển tiếp nối từ danh sách tuyến tính?

Từ khái niệm danh sách tuyến tính giới hạn vị trí thao tác (Restricted list), chương trình tiếp tục phát triển cấu trúc Ngăn xếp (Stack - LIFO) và Hàng đợi (Queue - FIFO), sau đó mở rộng sang các cấu trúc phi tuyến như Cây (Tree), Heap và Đồ thị (Graph).


Kết luận

Chương 4 "Lists" cung cấp hệ thống lý thuyết chặt chẽ về cấu trúc dữ liệu danh sách tuyến tính, bao gồm định nghĩa kiểu dữ liệu trừu tượng (ADT), hai mô hình cài đặt bộ nhớ chính (mảng liên tục và danh sách liên kết) cùng phương pháp phân tích độ phức tạp thuật toán bằng ký pháp Big-O. Các cài đặt bằng C++ Template và quản lý bộ nhớ cấp thấp trong bài giảng chuẩn hóa nền tảng tư duy lập trình và giải thuật, phục vụ trực tiếp cho việc học tập các cấu trúc dữ liệu nâng cao tiếp theo.