Linked list
Nội dung bài · 5 mục
- 1.Khái niệm
- 2.Ví dụ
- 3.Thử ngay
- 4.Lỗi hay gặp
- 5.Tóm tắt
Chèn một đơn gấp vào đầu ArrayList thì mọi đơn phía sau phải dời chỗ, tốn
O(n). Linked list không xếp phần tử liền nhau, nên chèn vào đầu chỉ mất một
bước. Đổi lại, muốn tới phần tử thứ 1000 thì phải đi qua 999 phần tử trước.
Khái niệm
⛓️ Linked list: danh sách mà mỗi phần tử (gọi là node) giữ giá trị và tham chiếu tới node kế tiếp, node cuối trỏ tới null.
Tham chiếu tới node kế tiếp (field next trong ví dụ) không chứa node đó,
mà chỉ trỏ tới nó, như bài Kiểu nguyên thuỷ và kiểu tham chiếu của khoá Java
Core.
Ví dụ
// Main.java
void main() {
var first = new Node("DH1");
first.setNext(new Node("DH2"));
first.getNext().setNext(new Node("DH3"));
Node current = first;
while (current != null) {
System.out.println(current.getValue());
current = current.getNext();
}
}
// Node.java
class Node {
private String value;
private Node next;
public Node(String value) {
this.value = value;
}
public String getValue() { return value; }
public Node getNext() { return next; }
public void setNext(Node next) {
this.next = next;
}
}- Mỗi
Nodegiữvaluevànext.nextcủaDH3chưa được gán nên lànull, đánh dấu hết danh sách. - Duyệt bằng cách đi theo
getNext()từ node đầu, tới khi gặpnull. - Biến kiểu
Nodenhận đượcnullnhư mọi kiểu tham chiếu, như bài null và Optional của khoá Java Core.
| Thao tác | ArrayList |
Linked list |
|---|---|---|
Lấy phần tử thứ i |
O(1) | O(n), đi từ đầu |
| Thêm vào đầu | O(n), dời cả list | O(1), đổi một tham chiếu |
| Thêm vào cuối | O(1) trung bình | O(1) nếu giữ node cuối |
Java có sẵn LinkedList với addFirst, addLast, removeFirst,
removeLast, đều O(1).
LinkedList thật nối node theo cả hai chiều: mỗi node có next và prev.
Nhờ vậy get(i) đi từ đầu nào gần vị trí i hơn.
▶ Mô phỏng: LinkedList đi tìm từng node
var orders = new LinkedList<String>();
orders.addLast("DH1");
orders.addLast("DH2");
orders.addLast("DH3");
orders.addLast("DH4");
System.out.println(orders.get(1));
System.out.println(orders.get(3));
orders.removeFirst();Xem từng bước
- addLast(DH1): list đang rỗng, first và last cùng trỏ tới node DH1.
- addLast(DH2): next của DH1 trỏ tới node mới, prev của nó trỏ về DH1, last trỏ tới nó. Đổi 3 tham chiếu: O(1).
- addLast(DH3): next của DH2 trỏ tới node mới, prev của nó trỏ về DH2, last trỏ tới nó. Đổi 3 tham chiếu: O(1).
- addLast(DH4): next của DH3 trỏ tới node mới, prev của nó trỏ về DH3, last trỏ tới nó. Đổi 3 tham chiếu: O(1).
- get(1): không nhảy thẳng được, bắt đầu từ first: đang ở DH1, vị trí 0.
- Tới vị trí 1: get(1) ra DH2 sau 1 bước đi theo next. Không có ô đánh số nên phải đi từng node: O(n).
- Tới vị trí 3: get(3) ra DH4 sau 0 bước đi theo prev. Không có ô đánh số nên phải đi từng node: O(n).
- removeFirst(): bỏ node DH1, first chuyển sang DH2. Chỉ đổi tham chiếu: O(1).
Thử ngay
Thêm đơn gấp DH0 vào đầu danh sách. Đặt ba dòng này ngay trên dòng
Node current = first;:
var urgent = new Node("DH0");
urgent.setNext(first);
first = urgent;Đoán trước khi chạy: danh sách in ra theo thứ tự nào? Có node nào phải dời chỗ không?
Xem kết quả
DH0
DH1
DH2
DH3DH0 đứng đầu nhưng không node nào phải dời. Chỉ có hai tham chiếu đổi:
next của urgent trỏ tới DH1, và first trỏ tới urgent.
Lỗi hay gặp
Lấy phần tử theo vị trí trên LinkedList. LinkedList có get(i),
nhưng không có ô đánh số: mỗi lần gọi phải đi qua từng node tới vị trí i.
Gọi get(i) trong vòng lặp là O(n²).
// SAI — mỗi get(i) lại đi qua từng node
var orders = new LinkedList<String>();
orders.addLast("DH1");
orders.addLast("DH2");
for (int i = 0; i < orders.size(); i++) {
System.out.println(orders.get(i));
}// ĐÚNG — cần lấy theo vị trí thì dùng ArrayList
var orders = new ArrayList<String>();
orders.add("DH1");
orders.add("DH2");
for (int i = 0; i < orders.size(); i++) {
System.out.println(orders.get(i));
}Tóm tắt
- Linked list gồm các node, mỗi node giữ giá trị và tham chiếu tới node kế.
- Thêm, bớt ở đầu là O(1). Lấy phần tử thứ
ilà O(n). - Java có
LinkedList.get(i)có, nhưng là O(n). - Trong thực tế,
ArrayListthường nhanh hơn vì các ô nằm liền nhau. Chỉ dùng linked list khi thêm bớt ở đầu hoặc giữa thật nhiều.
Bình luận
Đang tải bình luận…