Simulation implementation of singly linked list
-
- 3.1 Concept and structure of linked list
- 3.2 Classification of linked lists
- 3.3 Implementation of Linked List Headless + One-way + Acyclic Linked List Addition, Deletion, Search and Modification Implementation
-
- 3.3.1 Definition of Linked List
- 3.3.2 Printing of linked list data
- 3.3.3 Tail insertion of linked list
- 3.3.4 Dynamic application of linked list space
- 3.3.5 Header of linked list
- 3.3.6 Tail deletion of linked list
- 3.3.7 Header deletion of linked list
- 3.3.7 Pre-insertion at any position in the linked list
- 3.3.8 Post-insertion at any position in the linked list
- 3.3.8 Deletion of any position in the linked list
- 3.3.9 Predeletion at any position in the linked list
- 3.3.10 Post deletion of any position in the linked list
- 3.3.11 Destruction of linked list
- 3.3.12 Summary of Linked Lists
3.1 Concept and structure of linked list
Concept: Linked list is a non-consecutive and non-sequential storage structure in physical storage structure. The logical order of data elements is realized through the link order of pointers in the linked list.
Notice:
1. As can be seen from the above figure, the chain structure is logically continuous, but not necessarily physically continuous
2. In reality, nodes are generally applied for from the heap
3. From the space applied for on the top, It is allocated according to a certain strategy, and the space for the two applications may or may not be continuous.
3.2 Classification of linked lists
In practice, the structure of linked lists is very diverse. There are 8 linked list structures in combination of the following situations:
1. One-way or two-way linked list
2. Linked list with or without head
3. Circular or acyclic linked list
There are two most commonly used: headless one-way acyclic linked list, headed two-way circular linked list
- Headless one-way acyclic linked list: The structure is simple, and it is generally not used to store data alone. In practice, it is more of a substructure of other data structures, such as hash buckets, graph adjacency lists, and so on. In addition, this structure appears a lot in the written test interview.
- Headed doubly circular linked list: The structure is the most complex and is generally used to store data separately. The linked list data structures used in practice are all leading doubly circular linked lists. In addition, although this structure is complex, it will be found that the structure will bring many advantages after using the code to implement, and the implementation is simple, and we will know after the code is implemented.
3.3 Implementation of Linked List Headless + One-way + Acyclic Linked List Addition, Deletion, Search and Modification Implementation
3.3.1 Definition of Linked List
typedef int SLTDataType;//
typedef struct SListNode
{
int data;//val,存储的数据,此处假设存储的数据为int型
struct SListNode* next;//存储下一个节点的位置
}SListNode,SLN;
3.3.2 Printing of linked list data
void SListPrint(SListNode* phead)
{
SListNode* cur = phead;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
3.3.3 Tail insertion of linked list
void SListPushBack(SListNode** pphead, SLTDataType x)
{
SListNode* newnode = BuySListNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
//找尾
SListNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newnode;
}
}
In the process of finding the end, be sure not to write the following code:
while(tail!=NULL)
{
tail = tail->next;
}
tail->next = newnode;
Of course, the above description is the case of tail deletion.
The tail insertion is actually similar. The tail insertion is like the above code. When it tail!=NULL
is not established, the tail is equal to empty, and then the assignment operation is performed. tail->next = newnode
This line of code is equivalent to the following code:
(*tail).next
, here is equivalent to dereferencing the null pointer, which is actually illegal access, and also attempts to illegally modify the data in the unauthorized memory, which will inevitably lead to the crash of the program. And it does not store the address of the new node in the previous node's next.
This place needs to understand the fundamental principle of linked list traversal:
The linked list is a relatively static data space stored in the heap area. We traverse by changing the data in the local variable tail in the stack area (that is, the address of each linked list node). The reason why we can access it through the tail variable And the reason for modifying the node data is because the data type of tail is SListNode*, that is, the pointer to the node. The type of the pointer determines the data type that can be accessed by dereferencing the pointer, so *tail can access the data of the node in the heap area and can be modified.
3.3.4 Dynamic application of linked list space
SListNode* BuySListNode(SLTDataType x)
{
SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
if (newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
else
{
newnode->data = x;
newnode->next = NULL;
}
return newnode;
}
3.3.5 Header of linked list
void SListPushFront(SListNode** pphead, SLTDataType x)
{
SListNode* newnode = BuySListNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
3.3.6 Tail deletion of linked list
Three situations need to be considered:
- null
- a node
- multiple nodes
Two ways to write:
The first:
void SListPopBack(SListNode** pphead)
{
assert(pphead);
if (*pphead == NULL)//空链表
{
return;
}
else if ((*pphead)->next == NULL)//一个节点
{
free(*pphead);//*pphead就是plist的值
*pphead = NULL;
}
else//多个节点
{
SListNode* tail = *pphead;
SListNode* prev = NULL;//为什么要置为空呢?因为这个地方相当于是指向第一个节点之前的节点,这个节点并不存在,设为空
while (tail->next != NULL)
{
prev = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
prev->next = NULL;
}
}
This way there is no problem when faced with only one node.
The second:
void SListPopBack(SListNode** pphead)
{
assert(pphead);
if (*pphead == NULL)//空链表
{
return;
}
else if ((*pphead)->next == NULL)//一个节点
{
free(*pphead);//*pphead就是plist的值
*pphead = NULL;
}
else//多个节点
{
SListNode* tail = *pphead;
while (tail->next->next != NULL)
{
tail = tail->next;
}
free(tail->next);//释放尾节点
tail->next = NULL;//将新尾节点的next置为NULL
}
}
3.3.7 Header deletion of linked list
void SListPopFront(SListNode** pphead)
{
assert(pphead);
if (*pphead == NULL)//空链表
{
return;
}
else//非空链表
{
SListNode* next = (*pphead)->next;//next作为临时变量存放的是被删除的节点中next存储的第二个节点的地址
free(*pphead);
*pphead = next;
}
}
3.3.7 Pre-insertion at any position in the linked list
void SListInsertBefore(SListNode** pphead, SListNode* pos,SLTDataType x)
{
assert(pphead);
if (*pphead == pos)//pos是第一个节点,相当于头插
{
SListPushFront(pphead, x);
}
else
{
SListNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
SListNode* newnode = BuySListNode(x);
prev->next = newnode;
newnode->next = pos;
}
}
3.3.8 Post-insertion at any position in the linked list
Two implementations:
method one:
void SListInsertAfter(SListNode* pos, SLTDataType x)
{
assert(pos);
SListNode* newnode = BuySListNode(x);
newnode->next = pos->next;
pos->next = newnode;
//这两行代码顺序是固定的,只能这个顺序,无法进行改变
}
Icon:
Method two:
void SListInsertAfter(SListNode* pos, SLTDataType x)
{
assert(pos);
SListNode* next = pos->next;
SListNode* newnode = BuySListNode(x);
newnode->next = next;
pos->next = newnode;
//这两行代码可以任意改变顺序,谁先谁后都不影响最后的结果
}
Icon:
3.3.8 Deletion of any position in the linked list
void SListErase(SListNode** pphead, SListNode* pos)
{
assert(pphead);
assert(pos);
if (pos == *pphead)//当pos为头节点的时候
{
SListPopFront(pphead);
}
else//当pos为非头节点的时候
{
SListNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
Icon:
3.3.9 Predeletion at any position in the linked list
void SListEraseBefore(SListNode** pphead, SListNode* pos)//pos即为任意位置
{
assert(pphead);
assert(pos);
if (pos == *pphead)
{
return;
}
else if(pos==(*pphead)->next)
{
SListPopFront(pphead);
}
else
{
SListNode* prev = *pphead;//prev用来存储pos的前一个位置的前一个位置
while (prev->next->next != pos)
{
prev = prev->next;
}
SListNode* next = prev->next;//保存pos前一个节点的地址
prev->next = prev->next->next;//将prev和pos的两个节点进行连接
free(next);//删除pos的前一个节点
}
}
3.3.10 Post deletion of any position in the linked list
void SListEraseAfter(SListNode* pos)
{
assert(pos);
SListNode* next = pos->next;
if (next == NULL)//当pos是最后一个节点的时候
{
return;
}
else
{
pos->next = next->next;
free(next);
next = NULL;
}
}
Icon:
3.3.11 Destruction of linked list
void SListDestory(SListNode** pphead)
{
assert(pphead);
SListNode* cur = *pphead;
SListNode* next = *pphead;//是为了存储cur下一个节点的地址,因为free(cur)之后,cur指针指向的内存中的数据可能已经称为乱码了,即不能再正常的使用
while (cur)
{
next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
3.3.12 Summary of Linked Lists
Summary: Singly linked list structure, suitable for head plug deletion. Insertion and deletion at the end or somewhere in the middle are not suitable. If you want to use a linked list structure to store data separately, it is more suitable to use a doubly linked list.
The meaning of singly linked list learning:
- The singly linked list will be used as a substructure of complex data structures we will learn later (adjacency list of graph, hash bucket)
- There will be many classic practice questions in the singly linked list, and there will be many related questions in the written test interview.