In the linear list, in addition to the important structure of sequential list, there is also the chain structure, which is also what we often call the chain list.
Generally, each node of the linked list is represented by defining a structure (class). Generally speaking, the nodes of the linked list will have data fields and address fields. The data field is used to store data, and the address field is used to link nodes.
Common linked list structures are:
- Leading node and non leading node
- Single pointer and double pointer
- Cyclic and non cyclic
The linked list structure is implemented in C language.
The code is also uploaded to Gitee
Single linked list
This is the implementation of the single linked list, which is a headless node non cyclic linked list.
Sub file implementation:. h file is the declaration of related functions, and. c file is the definition of functions.
. h file
//Implementation of one-way acyclic linked list without sentinel node #include<stdio.h> #include<stdlib.h> #include<assert.h> //Define the data type of the linked list typedef int SLListDataType; //Defines the nodes of the structure typedef struct SLListNode { SLListDataType data; struct SLListNode* next; }SLLN; //Dynamically apply for a node SLLN* BuySLListNode(SLListDataType x); //Destruction of single linked list void SLListDestory(SLLN* plist); //Single linked list printing void SLListPrint(SLLN* plist); //Single chain table tail insertion void SLListPushBack(SLLN** plist, SLListDataType x); //Single chain tail deletion void SLListPopBack(SLLN** plist); //Single chain meter insertion void SLListPushFront(SLLN** plist, SLListDataType x); //Single chain header deletion void SLListPopFront(SLLN** plist); //Single linked list to find a data SLLN* SLListFind(SLLN* plist, SLListDataType x); //The single linked list inserts x before the pos position, and the incoming pos should be the structure address void SLListInsert(SLLN** plist, SLLN* pos, SLListDataType x); //The single linked list inserts an x after the pos position void SLListInsertAfter(SLLN* plist, SLLN* pos, SLListDataType x); //The single linked list deletes the value at pos //To delete the pos location, you need to get the location of the previous node of the location first //In this way, we can connect the previous node with the next node //Therefore, the node behind the pos is usually deleted void SLListErase(SLLN** plist, SLLN* pos); void SLListEraseAfter(SLLN* plist, SLLN* pos);
The data type of the typedef node. The implementation in the function does not need to be changed after subsequent modification
Defines the structure type, including data field and pointer field
Nodes need to apply dynamically, so malloc function is used
To implement a printing function is to check whether the function is implemented correctly
The tail interpolation function is the last node added to the linked list
Tail deletion is to delete the last node of the linked list
Header insertion is to add a new node at the head of the linked list, and the header pointer points to the new node
Header deletion is to delete the node at the head of the linked list, and the header pointer points to the next node
Searching data is based on the value of the data field. Of course, it can also be designed to search according to specific subscripts
A single linked list inserts a node at a certain location, which does not need to move data like an array
. c Documents
//Implementation of one-way acyclic linked list without sentinel bit head node #include"SingleLinkedList.h" //Dynamically apply for a node and assign a value SLLN* BuySLListNode(SLListDataType x) { //Open up a node space SLLN* tmp = (SLLN*)malloc(sizeof(SLLN)); //Judge whether the returned pointer is null if (tmp == NULL) { exit(-1); } else { //Apply for one node at a time, put the data to be stored in the node, and the next pointer is null tmp->next = NULL; tmp->data = x; } return tmp; } //Destruction of single linked list void SLListDestory(SLLN** plist) { assert(*plist && plist); SLLN* p = *plist; while (*plist != NULL) { p = *plist; *plist = p->next; free(p); } } //Single linked list printing void SLListPrint(SLLN* plist) { //Determine whether the passed in pointer is null //assert(plist); while (plist != NULL) { //The print format should be determined according to the type of SLListDataType printf("%d->", plist->data); plist = plist->next; } printf("NULL\n"); } //Single chain table tail insertion //What is passed in here is the secondary pointer of the structure. The purpose is to modify the external structure pointer to point to the dynamically opened space void SLListPushBack(SLLN** plist, SLListDataType x) { //Determine whether the passed in secondary pointer is null assert(plist); SLLN* newnode = BuySLListNode(x); SLLN* p = *plist; if (*plist == NULL) { *plist = newnode; } else { while (p->next != NULL) { p = p->next; } p->next = newnode; } } //Single chain tail deletion //If the single linked list has only one node, there will be no nodes after tail deletion, so we need to set plist empty void SLListPopBack(SLLN** plist) { //Judge whether the passed structure pointer is empty. If it is empty, it means that there is no node and it does not need to be deleted assert(*plist); //Define two pointers to record the last node and the penultimate node SLLN* tail = *plist; SLLN* prev = NULL; while (tail->next != NULL) { prev = tail; tail = tail->next; } //If there is only one node in the single linked list, we can directly free it if (prev == NULL) { free(tail); *plist = NULL; } else { free(tail); prev->next = NULL; } } //Single chain meter insertion //Similarly, you need to pass in the secondary pointer void SLListPushFront(SLLN** plist, SLListDataType x) { SLLN* newnode = BuySLListNode(x); SLLN* p = *plist; if (*plist == NULL) { *plist = newnode; } else { newnode->next = p; *plist = newnode; } } //Single chain header deletion //You need to change the direction of the header pointer, so you also need to pass in the secondary pointer void SLListPopFront(SLLN** plist) { //Judge whether there is at least one node assert(*plist); SLLN* p = *plist; *plist = (*plist)->next; free(p); } //Single linked list to find a data //If found, return the address pointing to the node SLLN* SLListFind(SLLN* plist, SLListDataType x) { //Determine whether the passed in pointer is null assert(plist); SLLN* p = plist; //Exit the loop when p is a null pointer while (p) { if (p->data == x) { return p; } p = p->next; } return NULL; } //The single linked list inserts an x after the pos position void SLListInsertAfter(SLLN* plist, SLLN* pos, SLListDataType x) { //Determine whether the two passed in pointers are null assert(plist && pos); SLLN* p = plist; while (p != pos) { //If p is NULL, it means that the entire linked list has been traversed, but no suitable pos has been found if (p == NULL) { printf("Entered pos non-existent\n"); return; } p = p->next; } SLLN* newnode = BuySLListNode(x); newnode->next = p->next; p->next = newnode; } //Single linked list inserts x before pos position //Header insertion may occur, so you also need to pass in a secondary structure pointer here //At this time, you need to define two structure pointers, one pointing to pos and the other pointing to a node in front of pos void SLListInsert(SLLN** plist, SLLN* pos, SLListDataType x) { assert(plist && pos); SLLN* prev = NULL; SLLN* tail = *plist; SLLN* newnode = BuySLListNode(x); if (tail == pos) { //This is equivalent to header insertion. You can directly call the interface of header insertion newnode->next = *plist; *plist = newnode; } else { //Here, tail= Null prevents pos from being found after traversing the linked list //The design here is to insert the data at the end if pos is not found after traversing the linked list while (tail != pos && tail != NULL) { prev = tail; tail = tail->next; } prev->next = newnode; newnode->next = tail; } } //The single linked list deletes the value at pos //Header deletion may occur, so it is also necessary to pass in the secondary pointer to change the header node pointed to by the header pointer void SLListErase(SLLN** plist, SLLN* pos) { assert(plist && pos); SLLN* prev = NULL; SLLN* tail = *plist; if (tail == pos) { *plist = tail->next; free(tail); } else { while (tail != pos) { //Prevent pos from being invalid, so that no POS can be found after traversing the complete linked list assert(tail); prev = tail; tail = tail->next; } prev->next = tail->next; free(tail); } } void SLListEraseAfter(SLLN* plist, SLLN* pos) { SLLN* p = plist; SLLN* tmp = NULL; while (p != pos) { //It is not allowed to pass in invalid pos and delete data after the last node assert(p); p = p->next; } //It is not allowed to delete the next node of the last node assert(p->next); tmp = p->next; p->next = tmp->next; free(tmp); }
Points to note:
- When deleting the tail, you need to traverse the linked list to find the last node and the previous position of the last node, make the node of the previous position point to NULL, and then release the last node
- When inserting and deleting headers, the address of the first node in the linked list will be changed. We need to change the external header pointer accordingly, either by passing in the secondary pointer or by returning a new header node
- When inserting a node in the middle of the linked list, pay attention to the order of disconnecting the old node and the order of linking the new node. The old node must not be disconnected first, otherwise the second half of the disconnected part will not be found
- The addition, deletion, modification and query of a single linked list is not very convenient, so it is generally not used as a data storage structure, but the general topic is the single linked list, so it also needs to be understood
Double linked list
Double linked list means that each node has two pointers, one is the successor pointer to point to the next node, and the other is the precursor pointer to point to the previous node.
Here is also a circular linked list of the head node with sentinel bit. The head node does not need to store data. Its only function is that the next pointer always points to the real first node of the linked list.
. h file
#include<stdio.h> #include<stdlib.h> #include<assert.h> typedef int DataType; typedef struct ListNode { struct ListNode* prev; struct ListNode* next; DataType data; }ListNode; //Create a header node and return ListNode* ListCreat(); //Creation of linked list nodes ListNode* ListNodeBuy(DataType x); //Destruction of linked list void ListDestroy(ListNode* pHead); //Printing of linked list void ListPrint(ListNode* pHead); //Header insertion of linked list void ListPushFront(ListNode* pHead, DataType x); //Header deletion of linked list void ListPopFront(ListNode* pHead); //Tail insertion of linked list void ListPushBack(ListNode* pHead,DataType x); //Tail deletion of linked list void ListPopBack(ListNode* pHead); //Search of linked list ListNode* ListFind(ListNode* pHead, DataType); //Insert a node in front of the pos position in the linked list void ListInsert(ListNode* pos, DataType x); //Delete the node above the pos position void ListErase(ListNode* pos);
The related functions of the double linked list are defined in the. h file. The approximate function is similar to that of the single linked list
. c Documents
//Implementation of two-way circular linked list #include"List.h" //Create a header node and return ListNode* ListCreat() { ListNode* pHead = (ListNode*)malloc(sizeof(ListNode)); if (pHead == NULL) { exit(-1); } pHead->next = pHead; pHead->prev = pHead; return pHead; } //Creation of linked list nodes ListNode* ListNodeBuy(DataType x) { ListNode* plist = malloc(sizeof(ListNode)); if (plist == NULL) { exit(-1); } else { plist->data = x; plist->next = NULL; plist->prev = NULL; return plist; } } //Destruction of linked list void ListDestroy(ListNode* pHead) { assert(pHead); assert(pHead->next); ListNode* cur = pHead->next; while (cur != pHead) { ListNode* next = cur->next; free(cur); cur = next; } free(pHead); } //Printing of linked list void ListPrint(ListNode* pHead) { assert(pHead); ListNode* cur = pHead->next; while (cur != pHead) { printf("%d ", cur->data); cur = cur->next; } printf("NULL"); printf("\n"); } //Header insertion of linked list void ListPushFront(ListNode* pHead, DataType x) { ListNode* newnode = ListNodeBuy(x); pHead->next->prev = newnode; newnode->next = pHead->next; newnode->prev = pHead; pHead->next = newnode; } //Header deletion of linked list void ListPopFront(ListNode* pHead) { assert(pHead->next != pHead); ListNode* tmp = pHead->next; ListNode* Nexttmp = tmp->next; pHead->next = tmp->next; Nexttmp->prev = pHead; free(tmp); } //Tail insertion of linked list void ListPushBack(ListNode* pHead,DataType x) { ListNode* newnode = ListNodeBuy(x); ListNode* tail = pHead->prev; newnode->next = pHead; newnode->prev = tail; tail->next = newnode; pHead->prev = newnode; } //Tail deletion of linked list void ListPopBack(ListNode* pHead) { assert(pHead->prev != pHead); ListNode* tmp = pHead->prev; ListNode* Prevtmp = tmp->prev; } //Search of linked list ListNode* ListFind(ListNode* pHead, DataType x) { ListNode* cur = pHead->next; while (cur != pHead) { if (cur->data == x) { return cur; } else { cur = cur->next; } } return NULL; } //Insert a node in front of the pos position in the linked list void ListInsert(ListNode* pos, DataType x) { assert(pos); ListNode* newnode = ListNodeBuy(x); ListNode* posPrev = pos->prev; posPrev->next = newnode; newnode->prev = posPrev; newnode->next = pos; pos->prev = newnode; } //Delete the node above the pos position void ListErase(ListNode* pos) { assert(pos); //It is not allowed to delete the head node of sentinel position assert(pos != pos->next); ListNode* Next = pos->next; ListNode* Prev = pos->prev; Prev->next = Next; Next->prev = Prev; free(pos); }
It should be noted that:
- Because the double linked list can find the previous node and the latter node, it is not necessary to record the previous node at any time when deleting a node
- Because it is a circular linked list, when deleting the last node, you don't need to traverse the whole linked list to find the tail node. You only need to use the precursor pointer of the head node
- A two-way circular linked list is generally used to store data
Comparison of array structure and chain structure
Data structure and chain structure are linear in logical structure
However, due to their respective characteristics, the scenarios used are also different
Disadvantages of arrays:
- If there is not enough space, it needs to be expanded. The expansion consumes a lot
- Deleting data or inserting data in the middle or header requires moving data, which is expensive
- Generally, in order to avoid frequent capacity expansion, each capacity expansion is doubled, but this may also cause a waste of space
Advantages of arrays:
- It supports random access, and can support some algorithms that need orderly or random access, such as binary search, quick sorting, etc
- Compared with the single linked list, the tail delete data and insert data do not need to be traversed, which is more efficient
Disadvantages of linked list:
- In addition to storing data, each node also stores pointers, which consumes more space than arrays
- Random access is not supported. Searching for some data requires traversal
Advantages of linked list:
- More reasonable use of space, and apply for space when storage is needed, which will not cause a waste of space
- There is no need to move the data to delete the data in the header and middle