Menu
CoddyTech

Danh sách liên kết

Cập nhật lần cuối

Danh sách liên kết (linked list) lưu một dãy giá trị dưới dạng chuỗi các nút, trong đó mỗi nút chứa một giá trị và một con trỏ trỏ đến nút tiếp theo. Khác với mảng, các nút không nằm liền nhau trong bộ nhớ: bạn đi theo các con trỏ next để duyệt danh sách. Nhấn Phát ở trên để xem các nút được nối vào đầu và cuối, một giá trị được tìm bằng cách đi dọc chuỗi, và một nút bị gỡ ra bằng cách nối lại một con trỏ.

Chèn hoặc xóa ở đầu danh sách tốn O(1) vì bạn chỉ cần trỏ lại head. Đi đến một vị trí ở giữa hoặc ở cuối tốn O(n) vì bạn phải đi đến đó trước. Sự đánh đổi này, hai đầu rẻ nhưng không truy cập ngẫu nhiên được, chính là điểm phân biệt danh sách liên kết với mảng.

Độ phức tạp thời gian

Thao tácĐộ phức tạpGhi chú
Chèn vào đầuO(1)Trỏ lại head
Chèn vào cuốiO(n)Phải đi đến cuối trước (O(1) nếu có con trỏ tail)
Tìm kiếmO(n)Đi theo các con trỏ next
Xóa đầuO(1)Trỏ head qua nút đó
Truy cập theo chỉ sốO(n)Không truy cập ngẫu nhiên được

Danh sách liên kết và mảng

Khía cạnhDanh sách liên kếtMảng
Bộ nhớCác nút rải rác cùng con trỏMột khối liền nhau
Truy cập ngẫu nhiênO(n)O(1)
Chèn/xóa ở đầuO(1)O(n) (phải dời)
Thân thiện với cacheKémTốt

Ví dụ minh họa

Xây dựng danh sách [10, 20], thêm 5 vào đầu, rồi xóa 20:

BướcCấu trúcThao tác
Bắt đầuhead -> nullDanh sách rỗng
Chèn 10 vào đầuhead -> 10 -> nullTrỏ head đến một nút mới có next là head cũ (null)
Chèn 20 vào cuốihead -> 10 -> 20 -> nullĐi đến nút 10, đặt next của nó là một nút mới 20
Chèn 5 vào đầuhead -> 5 -> 10 -> 20 -> nullNút mới 5 trỏ đến head cũ 10; head giờ trỏ đến 5
Xóa 20head -> 5 -> 10 -> nullĐi đến 10, đổi next của nó từ 20 thành null; nút 20 bị tách khỏi danh sách

Khi nào nên dùng danh sách liên kết

Nên dùng khiTránh dùng khi
Bạn chèn hoặc xóa ở hai đầu (hoặc tại một nút đang giữ) nhiều hơn hẳn việc truy cập theo chỉ sốBạn cần truy cập ngẫu nhiên nhanh theo vị trí (truy cập chỉ số O(1))
Kích thước thay đổi nhiều và bạn không muốn tốn chi phí đổi kích thước hay sao chépBạn chạy các vòng lặp chặt, nơi tính cục bộ của cache quyết định hiệu năng
Bạn đang xây dựng hàng đợi, ngăn xếp hoặc danh sách kềBộ nhớ hạn hẹp: mỗi phần tử tốn thêm một con trỏ
Bạn cần tham chiếu ổn định đến các nút, không bị ảnh hưởng khi chèn ở chỗ khácBạn chủ yếu đọc và hiếm khi thay đổi, nên mảng đơn giản và nhanh hơn

Code Linked List

Một cách cài đặt Linked List gọn gàng, chạy được ngay bằng Python, JavaScript, Java, C++, C. Chọn một ngôn ngữ, sao chép code hoặc mở sẵn trong Coddy Playground.

Code Linked List bằng Python

Python
1class Node:2    def __init__(self, value):3        self.value = value4        self.next = None5
6
7class LinkedList:8    def __init__(self):9        self.head = None10
11    def append(self, value):12        node = Node(value)13        if self.head is None:14            self.head = node15            return16        current = self.head17        while current.next:18            current = current.next19        current.next = node20
21    def find(self, value):22        current = self.head23        while current:24            if current.value == value:25                return True26            current = current.next27        return False28
29    def delete(self, value):30        # Re-link the previous node around the match31        current, prev = self.head, None32        while current:33            if current.value == value:34                if prev is None:35                    self.head = current.next36                else:37                    prev.next = current.next38                return True39            prev, current = current, current.next40        return False41
42    def __str__(self):43        values, current = [], self.head44        while current:45            values.append(str(current.value))46            current = current.next47        return " -> ".join(values) + " -> None"48
49
50lst = LinkedList()51for value in [3, 7, 1, 9]:52    lst.append(value)53
54print("List:    ", lst)55print("find(7): ", lst.find(7))56lst.delete(1)57print("After delete(1):", lst)
Chạy code này trong Playground Python

Câu hỏi thường gặp về danh sách liên kết

Danh sách liên kết khác mảng như thế nào?
Mảng lưu các phần tử trong một khối liền nhau, cho phép truy cập ngẫu nhiên O(1) nhưng chèn/xóa tốn O(n) vì phải dời phần tử. Danh sách liên kết lưu các nút ở bất kỳ đâu trong bộ nhớ, nối với nhau bằng con trỏ, cho phép chèn/xóa O(1) tại vị trí đã biết nhưng truy cập tốn O(n) vì phải đi dọc chuỗi.
Khi nào nên dùng danh sách liên kết?

Danh sách liên kết phát huy thế mạnh khi bạn thường xuyên chèn hoặc xóa ở hai đầu (hoặc tại một nút đã có tham chiếu) và hiếm khi cần truy cập ngẫu nhiên, ví dụ hàng đợi, ngăn xếp, danh sách kề và LRU cache. Nếu cần truy cập nhanh theo chỉ số, mảng hoặc mảng động thường tốt hơn.

Độ phức tạp thời gian của danh sách liên kết là bao nhiêu?
Chèn hoặc xóa ở đầu tốn O(1). Tìm kiếm, hoặc đi đến cuối khi không có con trỏ tail, tốn O(n) vì bạn đi theo từng con trỏ next một. Không có truy cập ngẫu nhiên O(1): truy cập theo chỉ số trong danh sách liên kết tốn O(n).
Danh sách liên kết đơn và danh sách liên kết đôi khác nhau như thế nào?
Danh sách liên kết đơn chỉ lưu một con trỏ next cho mỗi nút, nên bạn chỉ đi được một chiều và muốn xóa một nút thì cần tham chiếu đến nút đứng trước nó. Danh sách liên kết đôi thêm con trỏ prev, cho phép duyệt ngược và xóa một nút đang giữ trong O(1), đổi lại mỗi nút tốn thêm một con trỏ và mỗi lần chèn, xóa phải cập nhật nhiều hơn.
Tại sao chèn vào cuối tốn O(n) trong khi chèn vào đầu chỉ tốn O(1)?
Chèn vào đầu chỉ cần nối lại con trỏ head, một công việc hằng số. Đi đến cuối nghĩa là phải đi theo các con trỏ next từ head đến tận cuối, tốn O(n). Giữ thêm một con trỏ tail riêng giúp chèn vào cuối cũng chỉ tốn O(1), đó là lý do các danh sách trong thực tế thường theo dõi cả hai đầu.
Danh sách liên kết có chèn O(1) ở mọi vị trí không?
Không, đây là một hiểu lầm phổ biến. Bản thân thao tác chèn chỉ tốn O(1) khi bạn đã có sẵn con trỏ đến nút đứng trước vị trí chèn. Tìm vị trí đó theo giá trị hoặc chỉ số vẫn tốn O(n) vì bạn phải đi dọc chuỗi để đến đó.
Hình minh họa các ngôn ngữ lập trình của Coddy

Thành thạo thuật toán cùng Coddy

Bắt đầu