In my last blog, I shared the related contents of the sequence table. In this blog, I want to introduce the related contents of the single chain table.
Code link of this blog: https://gitee.com/byte-binxin/data-structure/tree/master/SList_ two
Concept of linked list
Concept: linked list is a non continuous and non sequential storage structure in physical storage structure. The logical order of data elements is realized through the pointer link order in the linked list.
There are about three kinds of linked lists. Today I will mainly introduce the first one - single linked list. In the next blog, I will introduce you to the relevant contents of double linked list.
It is worth noting that:
1. It can be seen from the figure that the of the linked list is continuous in logic and not necessarily continuous in physics;
2. In reality, nodes are applied from the heap.
Implementation of linked list
Definition of a single node of a linked list
First, we define a single node:
typedef int SLDataType; typedef struct SListNode { SLDataType data; struct SListNode* next; }SListNode;
Just like this figure, one space is used to store data, and the other space is used to store the address of the next node.
Interface of linked list
I have brought out the main interfaces of the linked list, and we want to implement these interfaces:
//Print linked list void SListPrint(SListNode* phead); //Destroy linked list void SListDestory(SListNode** pphead); //Tail insertion void SListPushBack(SListNode** pphead, SLDataType x); //Tail deletion void SListPopBack(SListNode** pphead); //Head insert void SListPushFront(SListNode** pphead, SLDataType x); //Header deletion void SListPopFront(SListNode** pphead); //lookup SListNode* SListFind(SListNode* phead, SLDataType x); //Insert after any position void SListInsertAfter(SListNode* pos, SLDataType x); //Delete after any position void SListEraseAfter(SListNode* pos);
The implementation of these interfaces is relatively simple, but it is also worth our attention. While implementing the interface, we should analyze it in combination with the diagram, otherwise the program will collapse accidentally.
Tail interpolation of single linked list
First, let's implement a tail insertion interface. Tail insertion is to insert a node at the end of the linked list.
We should consider the following points when performing tail interpolation:
- At this time, we should discuss whether the linked list is empty, what we should do if it is empty, and what we should do if it is not empty;
- Whether the formal parameter received by this interface should be a level 1 pointer or a level 2 pointer;
- How to apply for nodes, whether on the heap or stack?
In order to better understand tail interpolation, let's take a look at a dynamic diagram:
Looking at the motion chart, let's think about the above problems,
The formal parameter should be a secondary pointer. Why? (all the changes related to the head node are, and the description will not be repeated later)
void SListPushBack(SListNode** pphead, SLDataType x);
When the linked list is empty, we need to apply for a node, and this node will become the head node. That is, the original linked list points to NULL. We want to change this point to the newly opened node. The node should be opened on the heap. Because the formal parameter is a temporary copy of the actual parameter, the pointer can only be changed through the secondary pointer, and the primary pointer cannot. Passing the first level pointer will not point to the head. See the figure below:
The code implementation is as follows:
void SListPushBack(SListNode** pphead, SLDataType x) { SListNode* newNode = BuySListNode(x); //1. The linked list is empty if (*pphead == NULL) { *pphead = newNode; } //2. The linked list cannot be empty else { SListNode* cur = *pphead; while (cur->next != NULL) { cur = cur->next; } cur->next = newNode; } }
Insert five numbers, code test results:
Here, I encapsulate the application node into a function, because the subsequent header insertion and post insertion at any position will also be used. The code implementation is as follows:
SListNode* BuySListNode(SLDataType x) { SListNode* newNode = (SListNode*)malloc(sizeof(SListNode)); newNode->data = x; newNode->next = NULL; return newNode; }
Tail deletion of single linked list
Tail deletion is nothing more than deleting a node at the end of the linked list. It sounds very simple, but there are many details we should pay attention to. We should discuss it in three cases:
- No node
- Only one node
- Two or more nodes
Of course, we also pass the secondary pointer here.
First look at the dynamic diagram demonstration:
Code implementation by case:
- No node
assert(*pphead);//Violent resolution if (*pphead == NULL) { return;//Gentle solution }
- 1 node
if ((*pphead)->next == NULL) { free(*pphead);//Freeing the space pointed to by the pointer *pphead = NULL; }
- 2 or more nodes
Define two variables prev and cur to remember the location of the current node and the location of the previous node, so as to free up node space.
SListNode* prev = NULL;//Previous node SListNode* cur = *pphead;//Current node while (cur->next != NULL) { prev = cur; cur = cur->next; } free(cur); cur = NULL; prev->next = NULL;
The complete code is implemented as follows:
void SListPopBack(SListNode** pphead) { //There are three cases //1. No node //2. There is only one node //3. Two or more nodes if (*pphead == NULL) { return; } else if ((*pphead)->next == NULL) { free(*pphead); *pphead = NULL; } else { SListNode* prev = NULL; SListNode* cur = *pphead; while (cur->next != NULL) { prev = cur; cur = cur->next; } free(cur); cur = NULL; prev->next = NULL; } }
Header insertion of single linked list
Header insertion is relatively simple. There is no need to consider whether the linked list is empty or not. It can be well realized as long as the transfer of secondary pointers is considered.
The code implementation is also relatively simple, as follows:
void SListPushFront(SListNode** pphead, SLDataType x) { SListNode* newNode = BuySListNode(x); newNode->next = *pphead; *pphead = newNode; }
Single chain header deletion
Header deletion should be discussed according to the situation:
- The linked list is empty
- Linked list is not empty
First look at the dynamic diagram demonstration:
Code implementation by case:
- The linked list is empty
assert(*pphead);//Violent resolution if (*pphead == NULL) { return;//Gentle solution }
- Linked list is not empty
SListNode* firstNode = *pphead; *pphead = (*pphead)->next; free(firstNode); firstNode = NULL;
The complete code is as follows:
void SListPopFront(SListNode** pphead) { if (*pphead == NULL) { return; } else { SListNode* firstNode = *pphead; *pphead = (*pphead)->next; free(firstNode); firstNode = NULL; } }
Printing of single linked list
Linked list printing is to traverse the linked list, but the traversal here is a little different from that of the array. The traversal of the linked list is to judge whether the current position is NULL, so it will not print or not, and move the current position through · cur = cur - > next ·.
The code implementation is as follows:
void SListPopFront(SListNode** pphead) { if (*pphead == NULL) { return; } else { SListNode* firstNode = *pphead; *pphead = (*pphead)->next; free(firstNode); firstNode = NULL; } }
Single linked list lookup
Search is to search through the given node address and return the address of the node. If it cannot be found, it will return NULL. Since there is no change in the address of the header node, the secondary pointer is not transmitted.
It is also through variable method, which is a little similar to the printing of single linked list. There is no more introduction here. Go directly to the code:
SListNode* SListFind(SListNode* phead, SLDataType x) { SListNode* cur = phead; while (cur != NULL) { if (cur->data == x) { return cur; } cur = cur->next; } return NULL; }
Insert after any position in the single linked list
We choose to insert in any position, because it is more convenient to implement, and it will not involve the problem of remembering the previous node in the linked list. We can simply implement it.
First look at the dynamic diagram demonstration:
The code implementation is as follows:
void SListInsertAfter(SListNode* pos, SLDataType x) { assert(pos); SListNode* newNode = BuySListNode(x); newNode->next = pos->next; pos->next = newNode; }
Delete any position in the single linked list
Like the above, they are deleted at any position. Because the implementation is relatively simple, I won't introduce them more.
First look at the dynamic diagram demonstration:
The code implementation is as follows:
void SListEraseAfter(SListNode* pos) { assert(pos); SListNode* next = pos->next; if (next == NULL) { return; } else { SListNode* nextNext = next->next; free(next); next = NULL; pos->next = nextNext; } }
Destruction of single linked list
The destruction of the linked list also depends on traversal, so the code is also directly put here:
void SListDestory(SListNode** pphead) { assert(pphead); SListNode* cur = *pphead; SListNode* next = NULL; while (cur) { next = cur->next;//Remember the next node in case it cannot be found free(cur); cur = next; } *pphead = NULL; }
summary
The single linked list is not continuous in space, but it is physically continuous. It can be taken on demand, but it does not support random access. Linked list and sequential list have their own advantages. In the next blog, I will share the knowledge of double linked list and summarize the differences between sequential list and linked list. You are welcome to leave a message in the comment area, like, support and correction~