III.4.2. DSLK vịng
DSLK vịng là DSLK mà nút cuối là nút kề trước của nút đầu.
Nếu cài đặt DSLK vịng bằng kiểu con trỏ thì con trỏ của nút cuối trỏ đến nút đầu tiên. Trong DSLK vịng, ta cĩ thể lấy bất cứ nút nào làm nút đầu tiên xuất phát. Cấu trúc dữ liệu cho mỗi nút của DSLK vịng hồn tồn giống như DSLK
đơn.
CList.Head CList.Tail
Một số thao tác cơ bản cho DSLK vịng sẽđược viết lại sau đây, các thao tác khác được xem như bài tập.
• Khởi tạo một DSLK vịng rỗng
LL CreateEmptyCLL ()
{ LL CList;
CList.Head = CList.Tail = NULL; return List;
}
• Kiểm tra một DSLK vịng cĩ rỗng hay khơng
int EmptyCLL(LL CList)
{
return(CList.Head == NULL && CList.Tail == NULL);
}
• Duyệt qua một DSLK vịng
int TraverseCLL(LL CList)
{ NodePointer CurrPtr = CList.Head if (EmptyCLL(CList)) return 0; do
{ XửLý (CurrPtr);
CurrPtr = CurrPtr->Next;
} while (CurrPtr->Next != Clist.Head); return 1;
}
III.4.3. DSLK đối xứng
Trong nhiều thao tác trên kiểu DSLK đơn, khi làm việc với một nút ta cần biết nút đứng kề trước của nĩ. Lý do là DSLK đơn chỉ cĩ một liên kết đi theo một chiều từ nút đứng trước đến nút đứng sau. Để tăng độ linh hoạt trong các thao tác trên DSLK, cĩ thể di chuyển từ đầu đến đuơi của danh sách hay ngược lại, ta xét kiểu DSLK đối xứng (hay DSLK kép) mà mỗi nút cĩ hai trường liên kết ngược chiều nhau, một liên kết chỉ đến nút đứng sau và liên kết kia chỉ đến nút đứng trước.
DList.Head Prev Data Next DList.Tail
• •
a. Cấu trúc dữ liệu biểu diễn DSLK đối xứng
Trong C hay C++, mỗi nút của DSLK đối xứng được cài đặt bởi cấu trúc sau:
typedef .... ElementType; // Kiểu dữ liệu cơ sở của mỗi phần tử
struct Dnode *Next, *Prev; } DNodeType;
typedef DNodeType *DNodePointer; typedef struct { DNodePointer Head, Tail;
} DLL; DLL DList;
b. Các thao tác cơ bản trên DSLK đối xứng
Các thao tác cơ bản về sau sẽ sử dụng thủ tục cấp phát động vùng nhớ cho một nút của DSLK đối xứng sau đây: • Cấp phát vùng nhớ chứa dữ liệu x cho một nút của DSLK đối xứng Head • x • Tail - Thuật tốn DNodePointer CreateNodeDLL (x) . Cấp phát vùng nhớ cho một nút new_ele;
. new_ele ->Data = x; new_ele ->Next = NULL; new_ele ->Prev = NULL; . Trả về new_ele; . Trả về new_ele;
- Cài đặt
DNodePointer CreateNodeDLL (ElementType x)
{ DNodePointer new_ele;
if ((new_ele = new DNodeType) ==NULL)
cout << “\nLỗi cấp phát vùng nhớ cho một nút mới !”; else { Gán(new_ele ->Data, x);
new_ele ->Next = new_ele ->Prev = NULL;
}return new_ele; return new_ele; } • Khởi tạo một DSLK đối xứng rỗng. - Thuật tốn DLL CreateEmptyDLL ()
. DList.Head = DList.Tail = NULL; . Trả về DList;
- Cài đặt
DLL CreateEmptyDLL ()
{ DLL List;
DList.Head = DList.Tail = NULL;
}
• Kiểm tra một DSLK đối xứng cĩ rỗng hay khơng
- Thuật tốn
Boolean EmptyDLL(DLL DList)
if (DList.Head == NULL)
// hay (DList.Head == NULL) && (DList.Tail == NULL). Tại sao ? Hãy so sánh !
Trả trị True; // DList rỗng; else Trả trị False; // DList khác rỗng;
- Cài đặt
int EmptyDLL(DLL DList)
{ return(DList.Head == NULL);
// hay return ((DList.Head == NULL) && (DList.Tail == NULL));
}
• Duyệt qua một DSLK đối xứng
Ta cĩ thể duyệt Dlist theo chiều thuận (hay ngược) tùy theo chiều con trỏ
Next (hay Prev).
- Thuật tốn TraverseLL(DList)
. CurrPtr = DList.Head; // hay CurrPtr = DList.Tail; . Trong khi chưa hết DSLK thực hiện:
{ XửLý nút được trỏ bởi CurrPtr;
CurrPtr = CurrPtr->Next; // chuyển đến nút kề sau // hay CurrPtr = CurrPtr->Prev; chuyển đến nút kề trước }
- Cài đặt
int TraverseDLL(DLL DList)
{ DNodePointer CurrPtr = DList.Head; // hay CurrPtr = DList.Tail; if (EmptyDLL(DList)) return 0;
else { while (CurrPtr != NULL) // hoặc while (CurrPtr) { XửLý (CurrPtr);
CurrPtr = CurrPtr->Next; // hay CurrPtr = CurrPtr->Prev;
}
return 1;
} }
void XửLý(DNodePointer CurrPtr)
{ // Xử lý nút CurrPtr tùy theo từng yêu cầu cụ thể
return ;
}
• Thêm một phần tử mới vào DSLK đối xứng
* Thêm một phần tử vào sau một nút được trỏ bởi con trỏ PredPtr
(nếu PredPtr == NULL thì chèn phần tử vào đầu DSLK)
DList.Head Prev Data Next DList.Tail
• •
3 2 1 4
PredPtr X
new_ele
- Thuật tốn: Thêm một nút new_ele vào sau một nút được trỏ bởi PredPtr
InsertNodeAfterDLL(&DList, new_ele, PredPtr)
. if (PredPtr)
{ new_ele->Next = PredPtr->Next; new_ele->Prev = PredPtr; PredPtr->Next = new_ele;
if (new_ele->Next) (new_ele->Next)->Prev = new_ele;
// else: trường hợp chèn new_ele vào đuơi DList, khơng cập nhật nút sau nút new_ele }
else // chèn new_ele vào đầu Dlist
{ new_ele->Next = DList.Head;
if (DList.Head) DList.Head->Prev = new_ele; // else DS rỗng !
DList.Head = new_ele; //cập nhật lại nút đầu DS }
// nếu chèn nút mới vào đuơi, cần cập nhật lại đuơi mới . if (PredPtr == DList.Tail) DList.Tail = new_ele;
- Cài đặt
void InsertNodeAfterDLL(DLL &DList, DNodePointer new_ele,DNodePointer PredPtr)
{ if (PredPtr)
{ new_ele->Next = PredPtr->next; new_ele->Prev = PredPtr; PredPtr->Next = new_ele;
if (new_ele->Next) (new_ele->Next)->Prev = new_ele; }
else { new_ele->Next = DList.Head;
if (DList.Head) DList.Head->Prev = new_ele; DList.Head = new_ele;
}
if (PredPtr == DList.Tail) DList.Tail = new_ele; return ;
• Thuật tốn: Thêm phần tử x vào sau một nút được trỏ bởi con trỏ PredPtr
DNodePointer InsertElementAfterDLL (&DList, x, PredPtr) . new_ele = CreateNodeDLL (x);
. if (new_ele ≠ NULL) Thêm nút new_ele vào sau nút được trỏ bởi PredPtr;
. Trả về trị new_ele;
- Cài đặt
DNodePointer InsertElementAfterDLL(DLL &DList,ElementType x,DNodePointer PredPtr)
{ DNodePointer new_ele;
if ((new_ele = CreateNodeDLL (x)))
InsertNodeAfterDLL (DList, new_ele, PredPtr); return (new_ele);
}
Tương tự, ta cĩ thao tác thêm một nút (hay phần tử) vào trước một nút được trỏ bởi con trỏSuccPtr (bài tập).
• Thêm một phần tử vào cuối một DSLK đối xứng