Implementation of singly linked list in C language and introduction of linked list

1. Why there is a linked list

Because our commonly used sequence table will have the following problems:

1. Insertion and deletion in the middle/head, the time complexity is O(N)
2. To increase capacity, you need to apply for new space, copy data, and release old space. There will be a lot of consumption.
3. The increase in capacity is generally a 2-fold increase, and there is bound to be a certain amount of space waste. For example, the current capacity is 100, and when it is full
, it will be increased to 200. We continue to insert 5 data, and no data will be inserted later, so 95 data spaces will be wasted.

In response to the problems in the above sequence table, someone designed the structure of the linked list. Below I will give a detailed introduction to the single linked list with the simplest structure in the linked list.

2. Introduction of Linked List

2.1 The concept and structure of linked list

Concept: Linked list is a non-sequential and non-sequential storage structure in physical storage structure . The logical order of data elements is realized through the linking order
of pointers in the linked list.

Structure: the combination of linked list logic diagram and physical diagram

 From the above figure, we can see that each node of the linked list contains a data field and a pointer field, the head node stores the address of the first node, and the pointer field of the last node is a null pointer . From the logical diagram, it seems that each node has an arrow pointing to the next node. From the physical diagram, it is because each node stores the address of the next node. In the structure of the linked list, it should be noted that:

1. As can be seen from the above figure, the chain structure is logically continuous, but not necessarily physically continuous.

2. Nodes in reality are generally applied for on the heap.

3. The space requested from the heap is allocated according to a certain strategy, and the space requested twice may be continuous or discontinuous.

2.2 Classification of linked lists

1. One-way or two-way

2. To lead or not to lead

3. Cyclic or non-cyclic

Although there are so many linked list structures, two structures are most commonly used in practice:

3. Implementation of single linked list

See the following code:


#pragma once
#include <stdio.h>
#include <stdlib.h>
typedef int SLTDateType;
typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SLTNode;

//打印链表的数据域的内容
void SListPrint(SLTNode* phead);

//尾插
void SListPushBack(SLTNode** pphead, SLTDateType x);

//头插
void SListPushFront(SLTNode** pphead, SLTDateType x);

//尾删
void SListPopBack(SLTNode** pphead);

//头删
void SListPopFront(SLTNode** pphead);

//查找/修改结点的值
SLTNode* SListFind(SLTNode* phead, SLTDateType x);

//在pos位置之前插入一个结点
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x);

//在pos位置之后插入一个结点
void SListInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDateType x);

//删除pos结点
void SListErase(SLTNode** pphead, SLTNode* pos);

//删pos的后一个结点
void SListEraseAfter(SLTNode* pos);

//销毁链表
void SListDestrory(SLTNode** pphead);
#include "Slist.h"

//创建一个新结点
SLTNode* BuyListNode(SLTDateType x)
{
    SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    if (newnode == NULL)
    {
        printf("malloc fail\n");
        exit(-1);
    }
    newnode->data = x;
    newnode->next = NULL;
    return newnode;
}

//打印链表的数据域的内容
void SListPrint(SLTNode* phead)
{
    SLTNode* cur = phead;
    while (cur != NULL)
    {
        printf("%d->", cur->data);
        cur = cur->next;
    }
    printf("NULL\n");
}

//尾插
void SListPushBack(SLTNode** pphead, SLTDateType x)
{
    SLTNode* newnode = BuyListNode(x);
    if (*pphead == NULL)
    {
        *pphead = newnode;
    }
    else
    {
        SLTNode* tail = *pphead;
        while (tail->next != NULL)
        {
            tail = tail->next;//找到最后一个结点
        }
        tail->next = newnode;
    }
    
}

//头插
void SListPushFront(SLTNode** pphead, SLTDateType x)
{
    SLTNode* newnode = BuyListNode(x);
    newnode->next = *pphead;
    *pphead = newnode;
}

//尾删
void SListPopBack(SLTNode** pphead)
{
    if (*pphead == NULL)
    {
        return;//链表为空直接返回
    }
    if ((*pphead)->next == NULL)//头结点就是尾节点
    {
        free(*pphead);
        *pphead = NULL;
    }
    else
    {
        SLTNode* tail = *pphead;
        SLTNode* prev = NULL;//最后会走到倒数第二个结点
        while (tail->next != NULL)
        {
            prev = tail;
            tail = tail->next;
        }
        free(tail);
        tail = NULL;
        prev->next = NULL;
    }
}

//头删
void SListPopFront(SLTNode** pphead)
{
    if (*pphead == NULL)//链表为空直接返回
    {
        return;
    }
    SLTNode* next = (*pphead)->next;
    free(*pphead);
    *pphead = next;//头结点指向原来的第二个结点
}

//查找x出现的结点
SLTNode* SListFind(SLTNode* phead, SLTDateType x)
{
    SLTNode* cur = phead;
    while (cur)
    {
        if (cur->data == x)
        {
            return cur;
        }
        else
        {
            cur = cur->next;
        }
    }
    return NULL;
}

//在pos位置之前插入一个结点
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
    SLTNode* newnode = BuyListNode(x);
    if (*pphead == pos)//头结点就是pos的位置
    {
        //头插
        newnode->next = *pphead;
        *pphead = newnode;
    }
    //找到pos前一个位置
    else
    {
        SLTNode* posPrev = *pphead;
        while (posPrev->next != pos)
        {
            posPrev = posPrev->next;
        }
        posPrev->next = newnode;
        newnode->next = pos;
    }
}

//在pos位置之后插入一个结点
void SListInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
    SLTNode* newnode = BuyListNode(x);
    //以下两行代码的顺序不能调换,若调换会形成类似互指的问题
    newnode->next = pos->next;
    pos->next = newnode;
}

//删除pos结点
void SListErase(SLTNode** pphead, SLTNode* pos)
{
    if (*pphead == pos)//头删
    {
        SListPopFront(pphead);
    }
    else
    {
        SLTNode* prev = *pphead;
        while (prev->next != pos)
        {
            prev = prev->next;
        }
        prev->next = pos->next;
        free(pos);
    }
}

//删除pos结点后的一个结点
void SListEraseAfter(SLTNode* pos)
{
    SLTNode* next = pos->next;
    pos->next = next->next;
    free(next);
    next = NULL;
}

//销毁链表
void SListDestrory(SLTNode** pphead)
{
    SLTNode* cur = *pphead;
    while (cur)
    {
        SLTNode* next = cur->next;
        free(cur);
        cur = next;
    }
    *pphead = NULL;
}
#include "Slist.h"


void TestSList()
{
    SLTNode* plist = NULL;
    //尾插结点
    SListPushBack(&plist, 1);
    SListPushBack(&plist, 2);
    SListPushBack(&plist, 3);
    SListPushBack(&plist, 4);
    SListPrint(plist);

    //头插结点
    SListPushFront(&plist, 1);
    SListPushFront(&plist, 2);
    SListPushFront(&plist, 3);
    SListPushFront(&plist, 4);
    SListPrint(plist);

    //尾删结点
    SListPopBack(&plist);
    SListPopBack(&plist);
    SListPrint(plist);

    //头删结点
    SListPopFront(&plist);
    SListPrint(plist);

    //查找数值2出现的结点
    SLTNode* pos = SListFind(plist, 2);
    int i = 1;
    while (pos)
    {
        printf("第%d个pos结点:%p->%d\n", i++, pos, pos->data);
        pos = SListFind(pos->next, 2);
    }

    //在pos2结点前插入一个结点
    SLTNode* pos2 = SListFind(plist, 1);
    if (pos2)
    {
        SListInsert(&plist, pos2, 30);
    }
    SListPrint(plist);

    //在pos3结点后插入一个结点
    SLTNode* pos3 = SListFind(plist, 3);
    if (pos3)
    {
        SListInsertAfter(&plist, pos3, 20);
    }
    SListPrint(plist);

    //删除pos4结点
    SLTNode* pos4 = SListFind(plist, 2);
    SListErase(&plist, pos4);
    SListPrint(plist);

    //删除pos5结点的后一个结点
    SLTNode* pos5 = SListFind(plist, 3);
    SListEraseAfter(pos5);
    SListPrint(plist);

    //销毁链表
    SListDestrory(&plist);
}

int main()
{
    TestSList();
    return 0;

}

Note: Some people may not understand why some parameters are passed as secondary pointers. When you need to modify the linked list, the parameter needs to pass the secondary pointer. If you need to modify the linked list and you use a first-level pointer to pass the parameter, then it is equivalent to re-opening up a space for the formal parameter to store the value in the passed first-level pointer. Then if you modify the newly opened space in the function, the actual parameters will not be changed. If you need to modify the first-level pointer in the function, the formal parameter needs to pass the second-level pointer.

Guess you like

Origin blog.csdn.net/m0_74265792/article/details/130438399