IV. DANH SÁCH LIÊN KẾT KÉP (DOUBLE LISTS)
T ạo danh sách liên kết kép rỗng
Giả sử DL là con trỏ quản lí danh sách liên kết kép thì khi khởi tạo danh sách rỗng ta cho con trỏ này trỏNULL (không cấp phát ô nhớ cho DL), tức là gán DL=NULL.
void MakeNull_List (DoubleList *DL){ (*DL)= NULL;
}
Kiểm tra danh sách liên kết kép rỗng
Rõ ràng, danh sách liên kết kép rỗng khi và chỉ khi chỉ điểm đầu danh sách không trỏ tới một ô xác định nào cả. Do đó ta sẽ kiểm tra DL = NULL.
int Empty (DoubleList DL){ return (DL==NULL);
}
Xóa một phần tử ra khỏi danh sách liên kết kép
Để xoá một phần tử tại vị trí p trong danh sách liên kết kép được trỏ bởi DL, ta phải chú ý đến các trường hợp sau:
- Danh sách rỗng, tức là DL=NULL: chương trình con dừng.
- Trường hợp danh sách khác rỗng, tức là DL!=NULL, ta phải phân biệt hai trường hợp Ô bị xoá không phải là ô được trỏ bởi DL, ta chỉ cần cập nhật lại các con trỏđể nối kết ô trước p với ô sau p, các thao tác cần thiết là (xem hình II.16):
Nếu (p->previous!=NULL) thì p->previous->next=p->next; Nếu (p->next!=NULL) thì p->next->previous=p->previous;
Xoá ô đang được trỏ bởi DL, tức là p=DL: ngoài việc cập nhật lại các con trỏ để nối kết các ô trước và sau p ta còn phải cập nhật lại DL, ta có thể cho DL trỏ đến phần tử trước nó (DL = p->Previous) hoặc đến phần tử sau nó (DL = p->Next) tuỳ theo phần tử nào có mặt trong danh sách. Đặc biệt, nếu danh sách chỉ có một phần tử tức là p->Next=NULL và p->Previous=NULL thì DL=NULL.
Hình II.16 Xóa phần tử tại vị trí p
void Delete_List (Position p, DoubleList *DL){ if (*DL == NULL) printf(”Danh sach rong”); else{
if (p==*DL) (*DL)=(*DL)->Next;
//Xóa phần tử đầu tiên của danh sách nên phải thay đổi DL else p->Previous->Next=p->Next; if (p->Next!=NULL) p->Next->Previous=p->Previous; free(p); } }
Thêm phần tử vào danh sách liên kết kép
Để thêm một phần tử x vào vị trí p trong danh sách liên kết kép được trỏ bởi DL, ta cũng cần phân biệt mấy trường hợp sau:
Danh sách rỗng, tức là DL = NULL: trong trường hợp này ta không quan tâm đến giá trị của p. Để thêm một phần tử, ta chỉ cần cấp phát ô nhớ cho nó, gán giá trị x vào trường Element của ô nhớ này và cho hai con trỏ previous, next trỏ tới NULL còn DL trỏ vào ô nhớ này, các thao tác trên có thể viết như sau:
DL=(Node*)malloc(sizeof(Node)); DL->Element = x;
DL->Previous=NULL; DL->Next =NULL;
Nếu DL!=NULL, sau khi thêm phần tử x vào vị trí p ta có kết quả như hình II.18
p->Previous p p->Next Hình II.17: Danh sách trước khi thêm phần tử x
p->Previous p p->Next
Hình II.18: Danh sách sau khi thêm phần tử x vào tại vị trí p (phần tử tại vị trí p cũ trở thành phần tử "sau" của x)
Lưu ý: các kí hiệu p, p->Next, p->Previous trong hình II.18 để chỉ các ô trước khi thêm phần tử x, tức là nó chỉ các ô trong hình II.17.
Trong trường hợp p=DL, ta có thể cập nhật lại DL để DL trỏ tới ô mới thêm vào hoặc để nó trỏđến ô tại vị trí p cũ như nó đang trỏ cũng chỉ là sự lựa chọn trong chi tiết cài đặt.
void Insert_List (ElementType X,Position p, DoubleList *DL){ if (*DL == NULL){ (*DL)=(Node*)malloc(sizeof(Node)); (*DL)->Element = X; (*DL)->Previous =NULL; (*DL)->Next =NULL; } else{ Position temp; temp=(Node*)malloc(sizeof(Node)); temp->Element=X; temp->Next=p; temp->Previous=p->Previous; if (p->Previous!=NULL) p->Previous->Next=temp; p->Previous=temp; } }