Singly Linked List (Danh sách liên kết đơn)
Danh sách liên kết đơn (Singly Linked List) là một ví dụ đơn giản và hiệu quả về cấu trúc dữ liệu động, sử dụng con trỏ để quản lý các phần tử. Hiểu biết về con trỏ và cấp phát bộ nhớ động là rất quan trọng để nắm bắt cách thức hoạt động của danh sách liên kết.
Bài viết này sẽ tập trung vào danh sách liên kết đơn để giúp bạn dễ dàng tiếp cận và hiểu rõ hơn.
Lý thuyết về danh sách liên kết
Danh sách liên kết có chức năng tương tự như mảng, cho phép thêm và xóa các phần tử ở bất kỳ vị trí nào. Tuy nhiên, có một số điểm khác biệt chính giữa hai cấu trúc dữ liệu này:
Tính năng | Mảng | Danh sách liên kết |
---|---|---|
Kích thước | Cố định, cần khai báo trước | Thay đổi linh hoạt khi thêm/xóa phần tử |
Cấp phát bộ nhớ | Tĩnh, cấp phát trong quá trình biên dịch | Động, cấp phát trong quá trình chạy |
Thứ tự & sắp xếp | Lưu trữ liên tục trên các ô nhớ | Lưu trữ trên các ô nhớ ngẫu nhiên |
Truy cập | Truy cập trực tiếp bằng chỉ số: O(1) | Duyệt từ đầu/cuối đến phần tử cần truy cập: O(n) |
Tìm kiếm | Tuyến tính hoặc nhị phân | Chỉ có thể tìm kiếm tuyến tính |
Danh sách liên kết là gì?
Danh sách liên kết đơn là một tập hợp các Node được phân bố động. Mỗi Node chứa hai thành phần:
- Data: Lưu trữ giá trị của phần tử
- Next: Con trỏ trỏ đến Node tiếp theo trong danh sách
Nếu con trỏ next
trỏ tới NULL
, nghĩa là đó là phần tử cuối cùng của danh sách.
Hình ảnh minh họa:
- Cấu trúc của một Node:
- Ví dụ về danh sách liên kết đơn đầy đủ:
Cài đặt danh sách liên kết đơn
Khai báo linked list
struct LinkedList {
int data;
struct LinkedList *next;
};
typedef struct LinkedList *node; // Dùng node thay cho struct LinkedList để code ngắn gọn hơn
Trong ví dụ này, data
lưu trữ giá trị nguyên (int). Bạn có thể sử dụng các kiểu dữ liệu khác như float
, char
, hoặc thậm chí là struct do bạn tự định nghĩa.
Tạo mới một Node
node CreateNode(int value) {
node temp; // Khai báo một node tạm thời
temp = (node)malloc(sizeof(struct LinkedList)); // Cấp phát bộ nhớ cho node
temp->next = NULL; // Cho next trỏ tới NULL (ban đầu node chưa liên kết với node nào)
temp->data = value; // Gán giá trị cho node
return temp; // Trả về node mới đã được khởi tạo
}
Thêm Node vào danh sách liên kết
Thêm vào đầu
node AddHead(node head, int value) {
node temp = CreateNode(value); // Tạo node mới với giá trị value
if (head == NULL) {
head = temp; // Nếu danh sách rỗng, node mới trở thành head
} else {
temp->next = head; // Trỏ next của node mới tới head hiện tại
head = temp; // Cập nhật head là node mới
}
return head; // Trả về head mới
}
Thêm vào cuối
node AddTail(node head, int value) {
node temp = CreateNode(value); // Tạo node mới
if (head == NULL) {
head = temp; // Nếu danh sách rỗng, node mới trở thành head
} else {
node p = head; // Duyệt danh sách từ đầu
while (p->next != NULL) {
p = p->next; // Di chuyển đến node cuối cùng (next trỏ tới NULL)
}
p->next = temp; // Gán next của node cuối cùng trỏ tới node mới
}
return head; // Trả về head
}
Thêm vào vị trí bất kỳ
node AddAt(node head, int value, int position) {
if (position == 0 || head == NULL) {
head = AddHead(head, value); // Thêm vào đầu nếu vị trí là 0 hoặc danh sách rỗng
} else {
int k = 1; // Biến đếm vị trí
node p = head;
while (p != NULL && k != position) {
p = p->next; // Duyệt đến vị trí cần chèn
++k;
}
if (k != position) {
head = AddTail(head, value); // Thêm vào cuối nếu vị trí vượt quá danh sách
} else {
node temp = CreateNode(value); // Tạo node mới
temp->next = p->next; // Liên kết node mới với node tiếp theo
p->next = temp; // Liên kết node hiện tại với node mới
}
}
return head; // Trả về head
}
Lưu ý:
- Vị trí chèn được tính từ 0.
- Thứ tự các thao tác trong hàm
AddAt
rất quan trọng. Nếu bạn gánp->next = temp
trước, bạn sẽ mất liên kết đến phần sau của danh sách.