1, Detailed explanation of error prone interface
1.1 linked list initialization
To initialize the linked list, you need to open a header node. The value in the header node has no meaning. The two pointers next and prev of the header node should point to themselves. For details, please jump to the third part to view the definition of structure.
The first step is to open up the corresponding space, so encapsulate a function BuyListNode to create a new node.
static LTNode* BuyListNode(LTDataType x) { LTNode* newnode = (LTNode*)malloc(sizeof(LTNode)); if (newnode == NULL) { printf("malloc fail\n"); exit(-1); } newnode->data = x; newnode->next = NULL; newnode->prev = NULL; return newnode; }
With static modification, it will only be called in this file.
Then adjust the next and prev of the new node to point to the header node itself.
LTNode* ListInit() { LTNode* phead = BuyListNode(0); phead->next = phead; phead->prev = phead; return phead; }
When calling the BuyListNode function, the parameter is 0. Because the value in the header node has no meaning, it can be assigned at will.
1.2 destruction of linked list
Calling this interface usually releases the memory before the end of the program and returns it to the operating system to avoid memory leakage.
Because it is a circular linked list, it can go on indefinitely, so controlling when to stop release is the key.
To consider special cases, if the parameter is a null pointer, you can directly assert and report an error.
If there is only one head node (not including a valid node), then you can directly free the U-turn node.
void ListDestroy(LTNode* phead) { assert(phead); if (phead->next == NULL) { free(phead); phead = NULL; return; } LTNode* cur = phead->next; while (cur != phead) { LTNode* next = cur->next; free(cur); cur = NULL; cur = next; } free(cur); cur = NULL; }
However, it should be noted that when calling this interface, the address of the chain header node is generally passed in directly (the corresponding formal parameter in the interface is only a temporary copy), rather than the secondary pointer. This will cause the header node pointer to become a wild pointer after calling this interface, that is, the pointing space has been released, so the caller needs to release it manually. This situation can be compared to the call of the free function. The ListErase interface is the same.
After using the first level pointer, there is a problem of wild pointer, but it maintains the consistency of the interface.
The problem of wild pointer is solved by using two-level pointer. However, from the perspective of interface design, it is a little confused.
1.3 deletion
The call of tail deletion must ensure that there are valid nodes in the linked list that do not include the head node, and the head node cannot be empty.
The function of judging whether the linked list is empty can be encapsulated as a function.
Tail deletion algorithm: the next of the previous node of the tail node points to the head node, the prev of the head node points to the previous node of the tail node, and then free the tail node.
bool ListEmpty(LTNode* phead) { assert(phead); return phead->next == phead; } void ListPopBack(LTNode* phead) { assert(phead); assert(!ListEmpty(phead)); LTNode* tail = phead->prev; LTNode* tailPrev = tail->prev; free(tail); tailPrev->next = phead; phead->prev = tailPrev; }
1.4 insert before any node
The efficiency of inserting a one-way linked list after any node is higher, because it does not need to find the tail through the first node, but the disadvantage is that it cannot insert the head (here refers to a headless linked list).
Taking the lead in two-way loop can avoid this disadvantage. Because the insertion efficiency is high no matter how, it is inserted in front of any node.
Algorithm: keep the previous node of the specified node, record it as prev, create a new node newnode, and change the pointer of the specified node pos, prev and newnode.
void ListInsert(LTNode* pos, LTDataType x) { assert(pos); LTNode* newnode = BuyListNode(x); LTNode* prev = pos->prev; prev->next = newnode; newnode->prev = prev; newnode->next = pos; pos->prev = newnode; }
2, Simple interface implementation
2.1 print function
void ListPrint(LTNode* phead) { assert(phead); LTNode* cur = phead->next; while (cur != phead) { printf("%d <=> ", cur->data); cur = cur->next; } printf("\n"); }
2.2 tail insertion
void ListPushBack(LTNode* phead, LTDataType x) { assert(phead); LTNode* newnode = BuyListNode(x); phead->prev->next = newnode; newnode->prev = phead->prev; phead->prev = newnode; newnode->next = phead; }
2.3 connector
void ListPushFront(LTNode* phead, LTDataType x) { assert(phead); LTNode* newnode = BuyListNode(x); LTNode* next = phead->next; phead->next = newnode; newnode->prev = phead; next->prev = newnode; newnode->next = next; }
2.4 tail deletion
void ListPopFront(LTNode* phead) { assert(phead); assert(!ListEmpty(phead)); LTNode* fNode = phead->next; LTNode* next = fNode->next; free(fNode); fNode = NULL; phead->next = next; next->prev = phead; }
2.5 calculate the number of effective nodes in the linked list
size_t ListSize(LTNode* phead) { assert(phead); size_t count = 0; LTNode* cur = phead->next; while (cur != phead) { count++; cur = cur->next; } return count; }
2.6 finding nodes by value
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; }
2.7 delete any node (except the head node)
void ListErase(LTNode* pos) { assert(pos); LTNode* prev = pos->prev; LTNode* next = pos->next; free(pos); pos = NULL; prev->next = next; next->prev = prev; }
3, Header files and related definitions
#pragma once #include<stdio.h> #include<assert.h> #include<stdlib.h> #include<stdbool.h> typedef int LTDataType; typedef struct ListNode { struct ListNode* next; struct ListNode* prev; LTDataType data; }LTNode; LTNode* ListInit(); void ListDestroy(LTNode* phead); void ListPrint(LTNode* phead); void ListPushBack(LTNode* phead, LTDataType x); void ListPushFront(LTNode* phead, LTDataType x); void ListPopBack(LTNode* phead); void ListPopFront(LTNode* phead); bool ListEmpty(LTNode* phead); size_t ListSize(LTNode* phead); LTNode* ListFind(LTNode* phead, LTDataType x); void ListInsert(LTNode* pos, LTDataType x); void ListErase(LTNode* pos);