• 【数据结构】单链表 | 详细讲解


    线性表顺序存储结构的优缺点

    顺序表优点

    • 无须为了表示中间的元素之间的逻辑关系而增加额外的存储空间;
    • 因为以数组形式存储,可以快速地存取表中任一位置的元素。

    顺序表缺点

    • 插入和删除操作需要移动大量元素,时间复杂度为O(N);
    • 当线性表长度变化较大时,难以确定存储空间的容量;
    • 造成存储空间的“碎片”。

    顺序存储结构不足的解决办法

    其实顺序表最大的缺点就是插入和删除时需要移动大量元素,这显然就需要耗费极大时间,因为相邻两元素的存储位置也具有邻居关系。它们编号是1,2,3,...,n,它们在内存中的位置也是挨着的,中间没有空隙,当然就无法快速介入,而删除后,当中就会留出空隙,自然需要弥补。

    那这样的话,我们反正都要让相邻的元素都留有足够余地,那么干脆所有的元素都不考虑相邻位置了,哪里有空位就到哪里,而只是让每个元素知道他下一个元素的位置在哪里,这样我们就可以在第一个元素时,就知道第二个元素的位置(内存地址),而找到它;在第二个元素时,就知道第三个元素的位置(内存地址),而找到它。这样所有的元素我们就都可以通过遍历而找到了。

    其实这个思路也就是链表存储思路,而本篇文章着重讲述链表中的单链表。

    线性表链式存储结构定义

    在链式结构中,除了要存储数据元素信息外,还要存储它的后继元素的存储地址。

    数据域:存储数据元素信息的域称为数据域;

    指针域:存储直接后继位置的域称为指针域。

    在指针域中存储的信息称为指针或链。数据域与指针域信息组成数据元素的存储映像,称为结点。

    单链表:n个结点链结成的链表,此链表中的每个结点中只包含一个指针域。

    单链表的代码实现以及分析 

    单链表的结构代码

    单链表中的结点,是由存放数据元素的数据域和存放后继结点地址的指针域组成的。所以,假设这个数据为val,存放后继结点地址的指针为next。

    1. typedef int SLNDataType;
    2. typedef struct SListNode
    3. {
    4. struct SListNode* next;
    5. SLNDataType val;
    6. }SLNode;

    单链表中的每一个节点都是一个结构体成员,也就是多个结构体构成了一条链表。这与顺序表不同,顺序表由于数组存储 ,所以就是一个结构体的数组中存储了多个节点。

    单链表的遍历打印

    1. void SLTPrint(SLNode* phead)
    2. {
    3. assert(phead);
    4. SLNode* cur = phead;
    5. while (cur != NULL)
    6. {
    7. printf("%d->", cur->val);
    8. cur = cur->next;
    9. }
    10. printf("NULL\n");
    11. }

    单链表建立新的头结点 

    在创建新的头结点时,需要用到malloc函数,关于malloc的使用方法,这里不做过多阐述,大家有什么不懂的,可以点击链接(单击即可查看)详细学习malloc的使用方法哦。

    C库函数中 void *malloc(size_t size) 分配所需的内存空间,并返回一个指向它的指针。

    malloc分配内存空间函数。

     

    1. SLNode* SLTCreateNode(SLNDataType x)
    2. {
    3. SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
    4. if (newnode == NULL)
    5. {
    6. perror("malloc fail\n");
    7. exit(-1);
    8. }
    9. newnode->next = NULL;
    10. newnode->val = x;
    11. return newnode;
    12. }

    单链表的尾插

    那如果这是一个空表的话,那么就直接让这个单链表为指针,指向新创建的节点。

     

    1. void SLTPushBack(SLNode** pphead, SLNDataType x)
    2. {
    3. assert(pphead);
    4. SLNode* Newnode = SLTCreateNode(x);
    5. if (*pphead == NULL)
    6. {
    7. *pphead = Newnode;
    8. }
    9. else
    10. {
    11. SLNode* tail = pphead;
    12. while (tail->next != NULL)
    13. {
    14. tail = tail->next;
    15. }
    16. tail->next = Newnode;
    17. }
    18. }

    单链表的头插

     

    1. void SLTPushFront(SLNode** pphead, SLNDataType x)
    2. {
    3. assert(pphead);
    4. SLNode* Newnode = SLTCreateNode(x);
    5. Newnode->next = *pphead;
    6. *pphead = Newnode;
    7. }

    单链表的尾删

     

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

    单链表的头删

     

     

    1. void SLTPopFront(SLNode** pphead)
    2. {
    3. assert(pphead);
    4. assert(*pphead);
    5. SLNode* cur = (*pphead)->next;
    6. //注意在单链表头删的时候,如果只有一个节点,那也是可以的,就让那个临时的为空
    7. free(*pphead);
    8. *pphead = cur;
    9. }

    单链表的查找x数据

     

    1. SLNode* SLTFind(SLNode* phead, SLNDataType x)
    2. {
    3. SLNode* cur = phead;
    4. while (cur)
    5. {
    6. if (cur->val == x)
    7. {
    8. return cur;
    9. }
    10. else
    11. {
    12. cur = cur->next;
    13. }
    14. }
    15. return NULL;
    16. }

    单链表在pos位置插入x的数

     

    1. SLNode* SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x)
    2. {
    3. assert(*pphead);
    4. assert(pphead);
    5. assert(pos);
    6. if (*pphead==pos)
    7. {
    8. SLTPushFront(pphead,x);
    9. }
    10. else
    11. {
    12. SLNode* prev = *pphead;
    13. while (prev->next != pos)
    14. {
    15. prev = prev->next;
    16. }
    17. SLNode* Newnode = SLTCreateNode(x);
    18. prev->next = Newnode;
    19. Newnode->next = pos;
    20. }
    21. }

    单链表删除pos位置上的值

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

    单链表的销毁

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

  • 相关阅读:
    瑞吉外卖 —— 7、手机验证码登录
    【K8S】kubernetes基础原理
    十大开源机器人 智能体
    # ruby安装设置笔记
    预警期刊数量再次刷新:文章一投就拒稿,投稿之前要牢牢记住这几点
    fatal: Not a git repository (or any parent up to mount point /home)解决方法
    【VRTK】【VR开发】【Unity】7-配置交互能力和向量追踪
    anaconda安装配置
    【FFMPEG】Windows下将ffmpeg编译成lib和dll完整教程
    Python实现并测试K-means聚类算法
  • 原文地址:https://blog.csdn.net/2301_78131481/article/details/134384613