[经验分享] 单链表基础全解析

[复制链接]
0|0
荣陶陶 发表于 2026-7-4 11:01 | 显示全部楼层 |阅读模式
nex, AD, he, No, OD, PP
一.定义:
链表由多个节点组成,尽管物理空间上每个节点位置可能不同,但逻辑顺序上每个节点两两之间相互联系,像小火车的每节车厢一样,我们可以对链表进行增删查改等操作。







因此,链表由两部分组成,一部分是我们要存储的数据,另一部分是结构体指针,指向下一个节点地址,从而将每个节点两两连接在一起。

struct SListNode
{
    int Data;
    struct SListNode* next;
};


二.代码实现
1.打印

void SLTPrint(SLTNode* phead)
{
        SLTNode* pcur = phead;//创建临时变量,避免影响phead变量
        while (pcur)//pcur!=NULL
        {
                printf("%d->", pcur->data);
                pcur = pcur->next;//因为物理上可能不连续,因此不能用循环变量i来遍历每个节点。
        }
        printf("NULL\n");
}



2.尾插

SLTNode* SLTBuyNode(SLTDataType x)
{
        SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
        if (newnode == NULL)
        {
                perror("malloc");
                return 1;
        }
        newnode->data = x;
        newnode->next = NULL;
        return newnode;
}
void SLTPushBack(SLTNode** pphead, SLTDataTypex)
    //*plist==**phead,plist=*phead,&plist=phead
{   //*pphead是指向第一个节点的指针
        assert(pphead);//二级指针,不能为空;一级指针可以为空,表示空节点
        //创建新节点:
        SLTNode* newnode=SLTBuyNode(x);
        //两种情况:
        if (*pphead == NULL)//特殊情况:当链表没有节点时,phead指针指向NULL
        {
                *pphead = newnode;//将phead与新节点连接
        }
        else
        {
                //找到原链表尾节点:
                SLTNode* ptail = *pphead;
                while (ptail->next)//注意不是ptail
                {
                        ptail = ptail->next;//遍历每个节点
                }
                //连接起来;
                ptail->next = newnode;
        }
}


3.头插

void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
        assert(pphead);
        SLTNode* newnode = SLTBuyNode(x);
        //*pphead是否为空,该代码都可以满足,因此不用if分开讨论
        newnode->next = *pphead;//连接
        *pphead = newnode;//改变*pphead
}


4.尾删

void SLTPopBack(SLTNode** pphead)
{
        assert(pphead && *pphead);//pphead不能为空否则不能解引用+*phead不能为空,因为节点不能为空
        if ((*pphead)->next == NULL)
        {
                free(*pphead);
                *pphead == NULL;
        }
        else
        {
                SLTNode* ptail = *pphead;//指向尾节点
                SLTNode* prev = *pphead;//指向尾节点前一个节点
                while (ptail->next)
                {
                        prev = ptail;//滞后一个节点
                        ptail = ptail->next;
                }
                free(ptail);
                ptail = NULL;
                prev->next = NULL;
        }
}


5.头删

void SLTPopFront(SLTNode** pphead)
{
        assert(pphead && *pphead);
        SLTNode* next = (*pphead)->next;//创建临时结构体指针变量,指向第二个节点
        free(*pphead);
        (*pphead) = next;
}


6.查找

SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
        SLTNode* pcur = phead;
        while (pcur)//遍历每个元素
        {
                if (pcur->data == x)
                {
                        return pcur;
                }
                pcur = pcur->next;
        }
        return NULL;
}


7.在指定位置之前插入数据

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
        assert(pphead && *pphead);//!!!!!
        assert(pos);
        //创建新节点
        SLTNode* newnode = SLTBuyNode(x);
        //当pos在第一个节点:头插
        if (*pphead == pos)
        {
                SLTPushFront(pphead, x);
        }
        else
        {
                SLTNode* prev = *pphead;
                while (prev->next != pos)//找到pos前一个节点
                {
                        prev = prev->next;
                }
                //连接
                newnode->next = pos;
                prev->next = newnode;
        }
}



8.在指定位置之后插入数据


void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
        assert(pos);
        //创建新节点
        SLTNode* newnode = SLTBuyNode(x);
        //连接
        newnode->next = pos->next;
        pos->next = newnode;
}


9.删除指定位置节点

void SLTErase(SLTNode** pphead, SLTNode* pos)
{
        assert(pphead && *pphead);
        assert(pos);
        //头删(特殊情况)
        if (pos == *pphead)
        {
                SLTPopFront(pphead);
        }
        else
        {
                SLTNode* prev = *pphead;
                while (prev->next != pos)
                {
                        prev = prev->next;
                }
                prev->next = pos->next;
                free(pos);
                pos = NULL;
        }
}



10.删除指定位置之后的节点

void SLTErazeAfter(SLTNode* pos)
{
        assert(pos && pos->next);
        SLTNode* del = pos->next;//一定要创建del临时变量
        pos->next = del->next;
        free(del);
        del = NULL;//别忘了
}


11.销毁

void SListDestroy(SLTNode** pphead)
{
        assert(pphead && *pphead);
        SLTNode* pcur = *pphead;
        while (pcur)
        {//双指针法
                SLTNode* next = pcur->next;
                free(pcur);
                pcur = next;
        }
        *pphead = NULL;
}



三.整体代码实现:
SList.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int SLTDataType;
typedef struct SlistNode
{
        SLTDataType data;
        struct SlistNode* next; //类型是错误的
}SLTNode;

//打印:
void SLTPrint(SLTNode* phead);

//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x);

//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x);

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

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

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);

//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);

//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);

//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos);

//删除pos之后的节点
void SLTErazeAfter(SLTNode* pos);

//销毁
void SListDestroy(SLTNode** pphead);



SList.c

#include"SList.h"

//打印:
void SLTPrint(SLTNode* phead)
{
        SLTNode* pcur = phead;//创建临时变量,避免影响phead变量
        while (pcur)//pcur!=NULL
        {
                printf("%d->", pcur->data);
                pcur = pcur->next;//因为物理上可能不连续,因此不能用循环变量i来遍历每个节点。
        }
        printf("NULL\n");
}

//尾插:
SLTNode* SLTBuyNode(SLTDataType x)
{
        SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
        if (newnode == NULL)
        {
                perror("malloc");
                return NULL;
        }
        newnode->data = x;
        newnode->next = NULL;
        return newnode;
}
void SLTPushBack(SLTNode** pphead, SLTDataType x)//*plist==**phead,plist=*phead,&plist=phead
{//*pphead是指向第一个节点的指针
        assert(pphead);//二级指针,不能为空;一级指针可以为空,表示空节点
        //创建新节点:
        SLTNode* newnode=SLTBuyNode(x);
        //两种情况:
        if (*pphead == NULL)//特殊情况:当链表没有节点时,phead指针指向NULL
        {
                *pphead = newnode;//将phead与新节点连接
        }
        else
        {
                //找到原链表尾节点:
                SLTNode* ptail = *pphead;
                while (ptail->next)//注意不是ptail
                {
                        ptail = ptail->next;//遍历每个节点
                }
                //连接起来;
                ptail->next = newnode;
        }
}

//头插:
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
        assert(pphead);
        SLTNode* newnode = SLTBuyNode(x);
        //*pphead是否为空,该代码都可以满足,因此不用if分开讨论
        newnode->next = *pphead;//连接
        *pphead = newnode;//改变*pphead
}

//尾删
void SLTPopBack(SLTNode** pphead)
{
        assert(pphead && *pphead);//pphead不能为空否则不能解引用+*phead不能为空,因为节点不能为空
        if ((*pphead)->next == NULL)
        {
                free(*pphead);
                *pphead = NULL;
        }
        else
        {
                SLTNode* ptail = *pphead;//指向尾节点
                SLTNode* prev = *pphead;//指向尾节点前一个节点
                while (ptail->next)
                {
                        prev = ptail;//滞后一个节点
                        ptail = ptail->next;
                }
                free(ptail);
                ptail = NULL;
                prev->next = NULL;
        }
}

//头删
void SLTPopFront(SLTNode** pphead)
{
        assert(pphead && *pphead);
        SLTNode* next = (*pphead)->next;//创建临时结构体指针变量,指向第二个节点
        free(*pphead);
        (*pphead) = next;
}

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
        SLTNode* pcur = phead;
        while (pcur)//遍历每个元素
        {
                if (pcur->data == x)
                {
                        return pcur;
                }
                pcur = pcur->next;
        }
        return NULL;
}

//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
        assert(pphead && *pphead);//!!!!!
        assert(pos);
        //创建新节点
        SLTNode* newnode = SLTBuyNode(x);
        //当pos在第一个节点:头插
        if (*pphead == pos)
        {
                SLTPushFront(pphead, x);
        }
        else
        {
                SLTNode* prev = *pphead;
                while (prev->next != pos)//找到pos前一个节点
                {
                        prev = prev->next;
                }
                //连接
                newnode->next = pos;
                prev->next = newnode;
        }
}

//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
        assert(pos);
        //创建新节点
        SLTNode* newnode = SLTBuyNode(x);
        //连接
        newnode->next = pos->next;
        pos->next = newnode;
}

//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
        assert(pphead && *pphead);
        assert(pos);
        //头删(特殊情况)
        if (pos == *pphead)
        {
                SLTPopFront(pphead);
        }
        else
        {
                SLTNode* prev = *pphead;
                while (prev->next != pos)
                {
                        prev = prev->next;
                }
                prev->next = pos->next;
                free(pos);
                pos = NULL;
        }
}

//删除pos之后的节点
void SLTErazeAfter(SLTNode* pos)
{
        assert(pos && pos->next);
        SLTNode* del = pos->next;//一定要创建del临时变量
        pos->next = del->next;
        free(del);
        del = NULL;//别忘了
}

//销毁
void SListDestroy(SLTNode** pphead)
{
        assert(pphead && *pphead);
        SLTNode* pcur = *pphead;
        while (pcur)
        {//双指针法
                SLTNode* next = pcur->next;
                free(pcur);
                pcur = next;
        }
        *pphead = NULL;
}


test.c

#include"SList.h"

void SLTtest1()
{
        //创建几个节点:malloc
        SLTNode* node1=(SLTNode*)malloc(sizeof(SLTNode));
        node1->data = 1;
        SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));
        node2->data = 2;
        SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));
        node3->data = 3;
        SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));
        node4->data = 4;
        //将4个节点连接起来
        node1->next = node2;
        node2->next = node3;
        node3->next = node4;
        node4->next = NULL;

        //链表打印
        SLTNode* plist = node1;
        SLTPrint(plist);

}

void SLTest2()
{
        SLTNode* plist = NULL;
        SLTPushBack(&plist, 1);//传址调用!!!
        SLTPushBack(&plist, 2);
        SLTPushBack(&plist, 3);
        SLTPushBack(&plist, 4);
        SLTPrint(plist);
        SLTPopBack(&plist);
        SLTPrint(plist);
        SLTPopFront(&plist);
        SLTPrint(plist);
        SLTNode* find = SLTFind(plist, 2);
        SLTInsert(&plist, find, 11);
        SLTPrint(plist);
        SLTNode* find2 = SLTFind(plist, 3);
        SLTErase(&plist, find2);
        SLTPrint(plist);
        SLTNode* find3 = SLTFind(plist, 11);
        SLTErazeAfter(find3);
        SLTPrint(plist);
        SListDestroy(&plist);
        SLTPrint(plist);
}
int main()
{
        //SLTtest1();
        SLTest2();
        return 0;
}


————————————————
版权声明:本文为CSDN博主「fffzd」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yyyzc_/article/details/157060081

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
您需要登录后才可以回帖 登录 | 注册

本版积分规则

151

主题

531

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部
0