The structure of two-way leading circular linked list is shown in the figure

Definition of bidirectional circular linked list structure
typedef int LTDataType; typedef struct ListNode { LTDataType data; struct ListNode* prev; struct ListNode* next; }LTNode;
Note that the structure definition does not reflect whether the linked list has rings.
The interfaces to be implemented for the two-way leading circular linked list are:
- Create linked list and return header node (initialization)
- Destroy
- Tail insertion
- Head insert
- Tail deletion
- Header deletion
- lookup
- Insert in the previous position of pos
- Delete pos node
Create linked list and initialize
Note that a header node (sentinel node) is created here, and no data is saved.
LTNode* IistInit() { LTNode* phead = (LTNode*)malloc(sizeof(LTNode)); phead->next = phead; phead->prev = phead; return phead; }
Open up new nodes
LTNode* BuyListNode(LTDataType x) { LTNode* newnode = (LTNode*)malloc(sizeof(LTNode)); newnode->data = x; newnode->next = NULL; newnode->prev = NULL; return newnode; }
Print bidirectional linked list
Note that the loop end condition is cur=phead
void ListPrint(LTNode* phead) { assert(phead); LTNode* cur = phead->next; while(cur != phead) { printf("%d ", cur->data); cur = cur->next; } printf("\n"); }
Bidirectional linked list tail insertion
The time complexity is O(1), which is more efficient than single linked list and the same as sequential list
void ListPushBack(LTNode* phead, LTDataType x) { assert(phead); LTNode* tail = phead->prev; LTNode* newnode = BuyListNode(x); tail->next = newnode; newnode->prev = tail; phead->prev = newnode; newnode->next = phead; }
Bidirectional linked list tail deletion
void ListPopBack(LTNode* phead) { assert(phead); assert(phead->next != phead); LTNode* tail = phead->prev; LTNode* tailPrev = tail->prev; free(tail); tailPrev->next = phead; phead->prev = tailPrev; }
Bidirectional chain header insertion
void ListPushFront(LTNode* phead, LTDataType x) { assert(phead); LTNode* newnode = BuyLsitNode(x); LTNode* next = phead->next; phead->next = newnode; newnode->prev = phead; newnode->next = next; next->prev = newnode; }
Bidirectional linked list header deletion
The prerequisite for deletion is that the linked list is not empty
void ListPopFront(LTNode* phead) { assert(phead); assert(phead->next != phead); LTNode* next = phead->next; LTNode* nextNext = next->next; phead->next = nextNext; nextNext->prev = phead; free(next); }
Bidirectional linked list lookup
Node with return value of x
NULL if not found
LTNode* ListFind(LTNode* phead, LTDataType x) { assert(phead); LTNode* cur = phead->next; while(cur != phead) { if(cur->data == x) { return cur; } cur = cur->next; } return NULL; }
Insert in the previous position of pos
The default pos is in the linked list
If it is not a two-way linked list, it cannot be implemented (but it can also be implemented without a header node)
void ListInsert(LTNode* phead, LTDataType x) { assert(pos); LTNode* posPrev = pos->prev; LTNode* newnode = BuyListNode(x); posPrev->next = newnode; newnode->prev = posPrev; newnode->next = pos; pos->prev = newnode; }
Delete pos node
Here, the default pos is in the linked list, and pos cannot be a sentinel node, so there are at least phead and pos in the linked list
void ListErase(LTNode* pos) { assert(pos); LTNode* posPrev = pos->prev; LTNode* posNext = pos->next; posPrev->next = posNext; posNext->prev = posPrev; free(pos); }
Destroy linked list
Before free, you still need to record a node. The difference from the single linked list is that the loop end condition and the free sentinel node are required. Note that phead is not set to null, and the user needs to set phead to null outside the function. Similar to free.
void LsitDestroy(LTNode* phead) { assert(phead); LTNode* cur = phead->next; while(cur != phead) { LTNode* next = cur->next; free(cur); cur = next; } free(phead); }
Reuse ListInsert to implement head and tail insertion
Head insert
void ListPushFront(LTNode* phead, LTDataType x) { ListInsert(phead->next, x); }
Tail insertion
void ListPushBack(LTNode* phead, LTDataType x) { ListInsert(phead, x); }
Implementation of header deletion and tail deletion by reusing LsitErase
Header deletion
Note that assertions need to be preserved.
Previous header deletion
assert(phead->next != phead);
It is used to ensure that the linked list is not empty. Here, it is ensured that the pos passed to ListErase is not phead
void ListPopFront(LTNode* phead) { assert(phead); assert(phead->next !=phead); ListErase(phead->next); }
Tail deletion
void ListPopBack(LTNode* phead) { assert(phead); assert(phead->next != phead); ListErase(phead->prev); }
summary
The time complexity of two-way circular linked list is O(1), which fully shows the advantages of linked list.
In addition to random access, the efficiency is higher than that of sequential table.
Because the previous node can be accessed, tail insertion, middle insertion, tail deletion and middle deletion are simpler than single linked lists.