• 纯c语言版单链表(上)


    目录

    一、链表的定义

    二、创建一个节点

    三、对链表进行头插

    四、对链表进行头插

    五、对链表进行头删

    六、对链表进行尾删

    七、对链表的打印


    一、链表的定义

    1. typedef int SLTDataType;
    2. typedef struct SListNode
    3. {
    4. SLTDataType data;
    5. struct SListNode* next;
    6. }SLTNode;

    为了往后使用数据类型的变换,这里通过typedef 给int换了一个名字,后面我们要换其他的数据类型可以直接吧第一行的int换成你想要的类型。

    结构体内data储存数据,next储存下一个节点的地址。

    我们接下来创建一个节点。

    二、创建一个节点

    1. SLTNode* BuyNode(SLTDataType x)
    2. {
    3. SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    4. if (newnode == NULL)//当内存申请失败了需要报错并停止运行
    5. {
    6. perror("malloc fail");
    7. exit(-1);
    8. }
    9. newnode->data = x;
    10. return newnode;
    11. }

     我们需要用到一个SLTNode *类型的函数,因为我们要返回创建节点的地址。

    这样我们就可以创建一个链表并在里面插入数据了

    1. int main()
    2. {
    3. SLTNode* plist = NULL;
    4. phead = BuyNode(1);
    5. }

     phead表示为头结点。

    三、对链表进行头插

    1. void PushFront(SLTNode** pphead, SLTDataType x)
    2. {
    3. assert(pphead);
    4. SLTNode *newnode=BuyNode(x);
    5. newnode->next = *pphead;
    6. *pphead = newnode;
    7. }

    这里要注意的是,用到了二级指针,之所以要用二级指针是因为:我们需要改变phead的值,所以我们必须要把plist的地址传进来。

    四、对链表进行头插

    这里需要分情况讨论:1、链表非空。2、链表为空。

    当链表非空的时候,我们只需要找到尾节点,然后接上新节点就可以了。

    但当链表为空的时候,我们就要让头指针指向新节点(需要改变头指针phead),所以还是需要用到二级指针。

    1. void SlistPushBack(SLTNode** pphead, SLTDataType x)
    2. {
    3. assert(pphead);
    4. SLTNode* newnode = BuyNode(x);
    5. if (*pphead == NULL)
    6. *pphead = newnode;
    7. else
    8. {
    9. //找到尾节点
    10. SLTNode* tail=*pphead;
    11. while (tail->next != NULL)
    12. {
    13. tail = tail->next;
    14. }
    15. tail->next = newnode;
    16. }
    17. }

    五、对链表进行头删

    这里我们设置了一个del变量储存将要删除的节点,每次删除节点我们都要free掉这个节点,不然会有内存泄露的风险。

    1. void SListPopFront(SLTNode** pphead)
    2. {
    3. assert(pphead);
    4. assert(*pphead);
    5. SLTNode* del = *pphead;
    6. *pphead = (*pphead)->next;
    7. free(del);
    8. del = NULL;
    9. }

    六、对链表进行尾删

    1. void SListPopBack(SLTNode** pphead)
    2. {
    3. assert(pphead);
    4. if (*pphead == NULL)
    5. return;
    6. //一个节点
    7. //多个节点
    8. if ((*pphead)->next == NULL)
    9. {
    10. free(*pphead);
    11. *pphead = NULL;
    12. }
    13. else
    14. {
    15. SLTNode* prev = NULL;
    16. SLTNode* tail = *pphead;
    17. while (tail->next != NULL)
    18. {
    19. prev = tail;
    20. tail = tail->next;
    21. }
    22. prev->next = NULL;
    23. free(tail);
    24. tail = NULL;
    25. }
    26. }

    因为尾结点需要找到为节点的前一个节点,并将其next值设置为NULL,所以需要分情况讨论。

    七、对链表的打印

    1. void SLTPrint(SLTNode* phead)
    2. {
    3. if (phead = NULL)
    4. return;
    5. SLTNode* cur = phead;
    6. while (cur)
    7. {
    8. printf("%d->", cur->data);
    9. cur = cur->next;
    10. }
    11. printf("NULL\n");
    12. }

    八、对链表的销毁

    与上面的删除同理,链表的节点都是malloc出来的,养成好习惯,把每个malloc的空间的返回给系统避免资源浪费。

    1. void SLTistDestory(SLTNode** pphead)
    2. {
    3. assert(pphead);
    4. SLTNode* cur = *pphead;
    5. while (cur)
    6. {
    7. SLTNode* next = cur->next;
    8. free(cur);
    9. cur = next;
    10. }
    11. *pphead = NULL;
    12. }

    最后记得把phead指向NULL(也就是*pphead),避免野指针的产生。

    九、对链表的查找 

    注意它的返回类型是节点的指针型,在没找到节点时返回NULL。

    1. SLTNode* SListFind(SLTNode* phead, SLTDataType x)
    2. {
    3. if (phead == NULL)
    4. return;
    5. SLTNode* cur=phead;
    6. while (cur)
    7. {
    8. if (cur->data == x)
    9. return cur;
    10. cur = cur->next;
    11. }
    12. return NULL;
    13. }

    十、对链表某个数据前插入

    我们需要通过上面的find函数,找到我们想在哪个元素前插入的位置(pos)。我们需要找到pos亲一个节点prev,然后再把新节点插入prev节点与pos节点之间。

    1. void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
    2. {
    3. assrtt(pphead);
    4. assert(pos);
    5. if (*pphead == pos)
    6. {
    7. SListPopFront(x);
    8. }
    9. else
    10. {
    11. SLTNode* prev = *pphead;
    12. while (prev->next != pos)
    13. prev = prev->next;
    14. SLTNode* newnode = BuyNode(x);
    15. newnode->next = pos;
    16. prev->next = newnode;
    17. }
    18. }

    十一、对链表某个数据后插入

    因为单向链表只能从前往后查找,所以我们需要从头结点一个个往下找,才能找到插入的位置。

    但是如果你想在某个元素后面插入数据的话,只需要往下找一个就行了。

    1. void SListInsertAfter(SLTNode* pos, SLTDataType x)
    2. {
    3. assert(pos);
    4. SLTNode* next = pos->next;
    5. SLTNode* newnode = BuyNode(x);
    6. newnode->next = next;
    7. pos->next = newnode;
    8. }

    十二、对链表某个位置删除

    需要分情况讨论,我这里用了两个指针,当链表只有一个元素的时候不需要两个指针,所以需要分情况讨论。

    1. void SListErase(SLTNode **pphead,SLTNode* pos)
    2. {
    3. assert(pphead);
    4. assert(pos);
    5. SLTNode* prev=*pphead;
    6. if (pos == prev)
    7. {
    8. *pphead = pphead->next;
    9. free(pos);
    10. pos = NULL;
    11. }
    12. else
    13. {
    14. while (prev->next != pos)
    15. {
    16. prev = prev->next;
    17. }
    18. prev->next = pos->next;
    19. free(pos);
    20. pos = NULL;
    21. }
    22. }

    思路就是把要删除位置的前一个(prev)找到,然后吧prev的next改成pos的next,最后释放pos

    后续还会有更多细节补充

  • 相关阅读:
    react笔记-03react-router篇
    2022 年十大 Python Web 开发框架
    最新漏洞:Spring Framework远程代码执行漏洞
    基于SpringBoot+Vue的疫苗接种管理系统
    Android13-图片视频选择器
    [第八篇]——Docker 容器使用之cas源码
    尚好房 07_前端房源展示
    C++二分查找算法:阶乘函数后 K 个零
    Python的常用排序算法实现
    C++ DAY08 异常
  • 原文地址:https://blog.csdn.net/qq_64484137/article/details/126218802