当前位置:   article > 正文

单链表<数据结构 C版>

单链表<数据结构 C版>

目录

概念

 链表的单个结点

 链表的打印操作

新结点的申请

尾部插入

头部插入

尾部删除

头部删除

查找

在指定位置之前插入数据

在任意位置之后插入数据

测试运行一下:

 删除pos结点

 删除pos之后结点

 销毁链表


概念

单链表是一种在物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接顺序实现的。

链表的每个结点有两个部分,分别是数据和指向下个结点的指针,每个链表的最后一个结点的下一个结点为NULL(不能对NULL解引用)。

放一张bit课件里的图,我觉得很形象:


 链表的单个结点

  1. typedef int SLDataType;//重定义一下在链表内存放的数据类型,方便后期对类型进行统一修改
  2. //链表的单个结点
  3. typedef struct SListNode {//Single List Node :链表结点
  4. SLDataType data;//存放的数据
  5. struct SListNode* next;//指向下一个结点的指针
  6. }SLNode;//重定义名字方便后期使用

 链表的打印操作

  1. //链表的打印操作
  2. void SLPrint(SLNode* phead) {
  3. assert(phead);//不能传入空指针
  4. SLNode* pcur = phead;
  5. //pointer cursor:指针光标,不让头结点丢失(虽然不会改变头结点的指向)
  6. while (pcur) {//等同于pcur!=NULL
  7. printf("%d->", pcur->data);//打印此结点的数据
  8. pcur = pcur->next;//使pcur指向下一个结点
  9. }
  10. printf("NULL\n");
  11. }

新结点的申请

后面会涉及到新结点的插入,申请新结点可以封装成一个函数,避免代码冗余

  1. //新结点的申请
  2. SLNode* SLBuyNode(SLDataType x) {
  3. SLNode* node = (SLNode*)malloc(sizeof(SLNode));
  4. if (!node) {//返回值为空,申请失败(一般是空间不足了),直接退出
  5. perror("malloc fail");
  6. exit(1);
  7. }
  8. node->data = x;//将数据赋给data
  9. node->next = NULL;//将下一个节点置为NULL;
  10. return node;
  11. }

尾部插入

  1. //尾部插入
  2. void SLPushBack(SLNode** pphead, SLDataType x) {
  3. //注意在这里我们传参的是二级指针,因为我们需要在函数内部改变头结点的指向
  4. assert(pphead);//不能传NULL
  5. //新结点的申请
  6. SLNode* node=SLBuyNode(x);
  7. if (*pphead == NULL) {
  8. *pphead = node;
  9. }
  10. else {
  11. SLNode* pcur = *pphead;
  12. while (pcur->next) {//找到next元素为NULL的结点,也就是为链表尾部
  13. pcur = pcur->next;
  14. }
  15. pcur->next = node;
  16. }
  17. }

测试运行一下:


头部插入

  1. //头部插入
  2. void SLPushFront(SLNode** pphead, SLDataType x) {
  3. assert(pphead);
  4. SLNode* node = SLBuyNode(x);
  5. //这里需要处理头结点为空的情况吗?不需要,因为没有涉及到解引用其元素
  6. node->next = *pphead;
  7. *pphead = node;
  8. }

测试运行一下:


尾部删除

  1. //尾部删除
  2. void SLPopBack(SLNode** pphead) {
  3. assert(*pphead && pphead);//
  4. if (!(*pphead)->next) {//处理只有一个元素的情况
  5. free(*pphead);
  6. *pphead = NULL;
  7. }
  8. else {
  9. SLNode* prev = NULL;
  10. SLNode* pcur = *pphead;
  11. while (pcur->next) {//找到尾结点前一个结点
  12. prev = pcur;
  13. pcur = pcur->next;
  14. }
  15. free(pcur);//将尾结点释放
  16. pcur = NULL;
  17. prev->next = NULL;//将尾结点的前一个结点的next元素置为NULL
  18. }
  19. }

测试运行一下:


头部删除

  1. //头部删除
  2. void SLPopFront(SLNode** pphead) {
  3. assert(*pphead && pphead);
  4. SLNode* next = (*pphead)->next;//保存头结点的下一个结点地址
  5. free(*pphead);//释放头结点
  6. *pphead = next;//使头结点指向下一个结点
  7. }

测试运行一下:


查找

在链表中想要对指定位置进行操作不能使用下标,所以我们必须找到指定位置的地址才能对其进行操作。

  1. //查找
  2. SLNode* SLFind(SLNode* phead, SLDataType x) {
  3. assert(phead);
  4. SLNode* pcur = phead;
  5. while (pcur) {
  6. if (pcur->data == x) {
  7. return pcur;
  8. }
  9. pcur = pcur->next;
  10. }
  11. return NULL;
  12. }

在指定位置之前插入数据

  1. //在指定位置之前插入数据
  2. void SLInsert(SLNode** pphead, SLNode* pos, SLDataType x) {
  3. assert(pphead && pos && *pphead);//pos和pphead不能为空,以及pphead指向空间不能为空
  4. if (*pphead == pos) {//处理头插的情况
  5. SLPushFront(pphead,x);
  6. }
  7. else {
  8. SLNode* node = SLBuyNode(x);
  9. SLNode* pcur = *pphead;
  10. while (pcur->next != pos) {//找到pos前一个结点
  11. pcur = pcur->next;
  12. }
  13. node->next = pcur->next;
  14. pcur->next = node;
  15. }
  16. }

测试运行一下:


在任意位置之后插入数据

  1. //在任意位置之后插入数据
  2. void SLInsertAfter(SLNode* pos, SLDataType x) {
  3. assert(pos);//pos不能为空
  4. SLNode* node = SLBuyNode(x);
  5. node->next = pos->next;
  6. pos->next = node;
  7. }

测试运行一下:


 删除pos结点

  1. //删除pos结点
  2. void SLErase(SLNode** pphead, SLNode* pos) {
  3. assert(*pphead && pphead && pos);
  4. if (*pphead == pos) {//处理头删的情况
  5. SLPopFront(pphead);
  6. }
  7. else {
  8. SLNode* pcur = *pphead;
  9. while (pcur->next!= pos) {
  10. pcur = pcur->next;
  11. }
  12. pcur->next = pos->next;
  13. free(pos);
  14. pos = NULL;
  15. }
  16. }

 测试运行一下:


 删除pos之后结点

  1. //删除pos之后结点
  2. void SLEraseAfter(SLNode* pos) {
  3. assert(pos && pos->next);//pos后面的结点也不能为空
  4. SLNode* next = pos->next;
  5. pos->next = next->next;
  6. free(next);
  7. next = NULL;
  8. }

 测试运行一下:

 


 销毁链表

  1. //销毁链表
  2. void SLDestory(SLNode** pphead) {
  3. assert(pphead && *pphead);
  4. SLNode* pcur = *pphead;
  5. while (pcur) {
  6. SLNode* nextnode = pcur->next;//使用一个变量接受下一个结点地址
  7. free(pcur);
  8. pcur = nextnode;
  9. }
  10. *pphead = NULL;
  11. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/877436
推荐阅读
相关标签
  

闽ICP备14008679号