单链表——C语言实现

打印 上一主题 下一主题

主题 1774|帖子 1774|积分 5322

目次
一.相关指针知识点
二.链表
1.为什么学了顺序表还要学链表
2.优点
三.实现
1.链表的打印 —— 理解链表结构
(2) 物理结构图
2.链表的尾插 —— 入门
错误写法:tail != NULL
总结:
精确代码物理图解:
(2) 尾插团体代码 (思考对吗?)
Bug
3.头插
4.尾删
Bug
5.头删
6.查找
7. Find 查找的功能
(1)pos 之前插入
(2)pos 位置删除
(3)pos 之后插入
(4)pos位置后面删除
四.头脑提升
五.总结
1.传什么?
2.要不要断言?
(1)打印、查找
(2)pphead
(3)*pphead
六.团体代码
SList.h
SList.c

一.相关指针知识点

调用一个函数,就会建立一个空间,这个空间叫栈帧。局部变量是存放在栈帧里的(除了static修饰的局部变量)。函数竣事,栈帧空间就烧毁,局部变量也烧毁
函数传参,不管是传值,还是传地址,实在都是拷贝。就看拷贝值还是地址。
代码1:y 的改变,不会改变 x 的值
  1. void Func(int y)
  2. {
  3.         y = 1;
  4. }
  5. int main()
  6. {
  7.         int x = 0;
  8.         Func(x);
  9.         return 0;
  10. }
复制代码
这是两个栈帧,Func 里面是 y,main 里面是 x。x 传给 y 是拷贝给 y,y 的改变不会影响 x,而且 Func 会烧毁

代码2:办理上面题目,传地址。改变的是 int ,使用的是 int 的指针
  1. void Func(int* p)
  2. {
  3.         *p = 1;
  4. }
  5. int main()
  6. {
  7.         int x = 0;
  8.         Func(&x);
  9.         return 0;
  10. }
复制代码
这里的 p 是 x 地址的拷贝在传参里面,我们要改变什么,就要用它的指针。然后 * 解引用可以改变

代码3
  1. void Func(int* ptr)
  2. {
  3.         ptr = (int*)malloc(sizeof(int));
  4. }
  5. int main()
  6. {
  7.         int* px = NULL;
  8.         Func(px);
  9.     free(px); // 加上也没用
  10.         return 0;
  11. }
复制代码
这也是拷贝值,把 px 的值拷贝给 ptr,ptr 是空。但是我 malloc 了一块空间,让 ptr 指向这块空间。
px 拷贝给 ptr,ptr 的改变不会影响 px 。而且出了作用域 Func 烧毁,malloc 的内存块还找不到了(内存泄漏),就算 free 也 free 不到

这里我们要改变的是 int* ,不是 int 。传 int* 不起作用。应该传 int**(二级指针)
代码4:改变 int* ,使用 int* 的地址,int**(二级指针)
  1. void Func(int** pptr)
  2. {
  3.         *pptr = (int*)malloc(sizeof(int));
  4. }
  5. int main()
  6. {
  7.         int* px = NULL;
  8.         Func(&px);
  9.         free(px);
  10.         return 0;
  11. }
复制代码
这里把 px 的地址传过去,pptr 指向 px 。malloc了一块空间,是让 *pptr 即 px 指向这块空间
Func 竣事,栈帧烧毁。但 px 还指向这块空间,free 可以 free 到。这里内存开释,值也拿返来了

二.链表

1.为什么学了顺序表还要学链表

顺序表是有许多缺陷的:
1)中间,头部 插入,删除数据,必要挪动数据,服从低下。你也不可能说在中间插入一块空间,没有这种概念,这本来就是一块一连的空间。
(2)空间不敷必要扩容,拷贝数据,开释旧空间。会有不小的消耗
扩容有一定的服从消耗。原地扩还好,异地扩呢?
还可能会有一定的空间浪费。一次扩太少,会频繁扩;一次扩太多,浪费
能不能说,我用一点给一点呢?存一块数据,开一块空间
可以,但怎么管理呢?
顺序表里,开了整片空间,由于存放的数据是一连的,只必要记录这块空间最开始的地址。
现在要一块空间,去 malloc 。多次 malloc ,他们之间的地址不能保证相邻。
这时候,链表会用一个指针指向第一个内存块(节点 Node)。
为了通过第一个能找到第二个怎么办?上一个会存下一个的地址,上一个指向下一个。
什么时候竣事?顺序表是 size 。链表里末了一个节点的指针存 NULL 即可
2.优点

不必要扩容。存一块,开一块。
可以从中间插入,不必要挪动数据。
顺序表,链表是互补,相辅相成的。许多情况是共同起来使用的
三.实现

上面的理解,链表是一个个的内存块,再由指针链接起来
先来界说它的结构:从语言的角度来说,凡是有多个数据,都要存到结构体里面
为方便替换成其他范例的数据,我们将范例统一重命名为 SLTDataType
1.链表的打印 —— 理解链表结构

SList.h
上一个节点要存下一个节点的地址,每个节点都是结构体范例,以是存结构体指针 next
链表要有个头指针 phead 指向第一个节点,判定竣事只必要走到空 NULL 即可。
不能断言 phead 为空,空链表也可以打印
  1. typedef int SLTDataType;
  2. typedef struct SListNode
  3. {
  4.         SLTDataType data;
  5.         struct SListNode* next;
  6. }SLTNode;
  7. //打印链表
  8. void SLTPrint(SLTNode* phead);
复制代码
SList.c
  1. void SLTPrint(SLTNode* phead)
  2. {
  3.         SLTNode* cur = phead;
  4.         //while (cur->next != NULL) 错误写法!!!
  5.         //while(cur != NULL)
  6.         while (cur)
  7.         {
  8.                 printf("%d->", cur->data);
  9.                 cur = cur->next; // 指向下一个位置
  10.         // 不能写成 ++cur;
  11.         }
  12.         printf("NULL\n");
  13. }
复制代码

问:为什么不能写成 ++cur ?
答:链表地址不一连,++cur 不能保证它指向下一个位置。如果强行把地址弄成一连,不就成顺序表了吗?

怎么理解 cur = cur->next;
cur 是结构体指针,cur-> 就是访问结构体成员。next 是结构体成员,是下一个节点的地址
赋值操纵是把下一个节点的地址给 cur 


为什么循环判定条件 cur->next != NULL 为错?
cur->next 是下一节点地址。走到尾就竣事了,没有打印末了的数据

(2) 物理结构图

上面画的是逻辑结构图,是为方便理解,形象画出来的
物理结构图:实实在在数据在内存中的变化

2.链表的尾插 —— 入门

依然不能断言 phead 为空。为空(没有数据)依然可以尾插
顺序表尾插,先要判定空间够不敷,不敷扩容。      链表不用,永远有空间
第一步:搞个节点,并初始化。后面多次用到,分装成函数
第二步:找尾。  尾的特征:tail->next == NULL
  1. // 搞节点,并初始化
  2. SLTNode* BuySLTNode(SLTDataType x)
  3. {
  4.         SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
  5.         if (newnode == NULL)
  6.         {
  7.                 perror("malloc fail");
  8.                 return NULL;
  9.         }
  10.         // 初始化
  11.         newnode->data = x;
  12.         newnode->next = NULL;
  13.         return newnode;
  14. }
  15. void SLTPushBack(SLTNode* phead, SLTDataType x) // 思考这里对吗?
  16. {
  17.         SLTNode* newnode = BuySLTNode(x);
  18.         // 找尾
  19.         SLTNode* tail = phead;
  20.         while (tail->next != NULL)
  21.         {
  22.                 tail = tail->next;
  23.         }
  24.         tail->next = newnode;
  25. }
复制代码

错误写法:tail != NULL

  1. // 找尾
  2. SLTNode* tail = phead;
  3. while (tail != NULL)
  4. {
  5.         tail = tail->next;
  6. }
  7. tail = newnode;
复制代码
从逻辑结构图角度,看似精确:

从物理结构图理解:






tail ,newnode 都是局部变量,出了作用域烧毁

上一个节点没有存下一个节点的地址,链接失败


总结:

tail 是个局部变量。不应该赋值给 tail 。应该赋值给 tail 指向的结构体(存放下一个节点地址的)成员
不为空链表尾插的本质:原尾节点中要存新的尾节点的地址


精确代码物理图解:

  1. // 找尾
  2. SLTNode* tail = phead;
  3. while (tail->next != NULL)
  4. {
  5.         tail = tail->next;
  6. }
  7. tail->next = newnode;
复制代码






tail ,newnode 都是局部变量,出了作用域烧毁

上一个节点存储下一个节点的地址,链接成功
(2) 空链表尾插
phead == NULL      tail = phead        让 phead 指向新节点即可

(2) 尾插团体代码 (思考对吗?)

SList.h
  1. typedef int SLTDataType;
  2. typedef struct SListNode
  3. {
  4.         SLTDataType data;
  5.         struct SListNode* next;
  6. }SLTNode;
  7. //打印链表
  8. void SLTPrint(SLTNode* phead);//尾插void SLTPushBack(SLTNode* phead, SLTDataType x); // 思考这里对不对
复制代码
SList.c 
  1. void SLTPushBack(SLTNode* phead, SLTDataType x) // 对吗?
  2. {
  3.         SLTNode* newnode = BuySLTNode(x);
  4.         if (phead == NULL)
  5.         {
  6.                 phead = newnode;
  7.         }
  8.         else
  9.         {
  10.                 // 找尾
  11.                 SLTNode* tail = phead;
  12.                 while (tail->next != NULL)
  13.                 {
  14.                         tail = tail->next;
  15.                 }
  16.                 tail->next = newnode;
  17.         }
  18. }
复制代码
Test.c
  1. void TestSList1()
  2. {
  3.         SLTNode* plist = NULL;
  4.         SLTPushBack(plist, 1);
  5.         SLTPushBack(plist, 2);
  6.         SLTPushBack(plist, 3);
  7.         SLTPushBack(plist, 4);
  8.         SLTPrint(plist);
  9. }
复制代码
Bug

我们运行上面的代码:

看下图,phead 和 newnode 都是结构体指针范例的指针变量
phead = newnode 是赋值行为,其真正寄义是让 phead 也指向 newnode 指向的新节点

函数竣事,栈帧空间烧毁。我们的目标是让 plist 指向新节点,但末了没有,造成了内存泄漏

改Bug
我们要改变 SLNode* plist ,传参里要传 SLNode* plist 的地址 ,用 SLNode** 接收
SList.h
  1. typedef int SLTDataType;
  2. typedef struct SListNode
  3. {
  4.         SLTDataType data;
  5.         struct SListNode* next;
  6. }SLTNode;
  7. //打印链表
  8. void SLTPrint(SLTNode* phead);//尾插void SLTPushBack(SLTNode** pphead, SLTDataType x);
复制代码
SList.c
  1. void SLTPushBack(SLTNode** pphead, SLTDataType x)
  2. {
  3.         assert(pphead);
  4.         SLTNode* newnode = BuySLTNode(x);
  5.         if (*pphead == NULL)
  6.         {
  7.                 *pphead = newnode;
  8.         }
  9.         else
  10.         {
  11.                 // 找尾
  12.                 SLTNode* tail = *pphead;
  13.                 while (tail->next != NULL)
  14.                 {
  15.                         tail = tail->next;
  16.                 }
  17.                 tail->next = newnode;
  18.         }
  19. }
复制代码
pphead 存的是 plist 的指针。*pphead 就是 plist 。
函数竣事,栈帧空间烧毁。plist 指向了新节点

链表运行结果:

3.头插

盲猜头插要用二级指针,因为一定有一个情况是为空,为空肯定要用

  1. void SLTPushFront(SLTNode** pphead, SLTDataType x)
  2. {
  3.         assert(pphead);
  4.         SLTNode* newnode = BuySLTNode(x);
  5.         newnode->next = *pphead;
  6.         *pphead = newnode;
  7. }
复制代码
如果传的是 phead ,改变的就是 phead ,无法改变外边的 plist
这段代码同样可以办理空的情况
4.尾删

SList.c
  1. void SLTPopBack(SLTNode** pphead) // 这么写对吗?
  2. {
  3.     assert(pphead);
  4.     assert(*pphead);
  5.         // 找尾
  6.         SLTNode* tail = *pphead;
  7.         while (tail->next != NULL)
  8.         {
  9.                 tail = tail->next;
  10.         }
  11.         free(tail);
  12.         tail = NULL;
  13. }
复制代码
Test.c
  1. void TestSList1()
  2. {
  3.         SLTNode* plist = NULL;
  4.         SLTPushFront(&plist, 1);
  5.         SLTPushFront(&plist, 2);
  6.         SLTPushFront(&plist, 3);
  7.         SLTPushFront(&plist, 4);
  8.         SLTPrint(plist);
  9.         SLTPopBack(&plist);
  10.         SLTPrint(plist);
  11. }
复制代码

Bug

     碰上这种情况多半是野指针,调试看看

尾就是1这个节点,2这个节点存着他的地址

直接把 tail 指向的尾节点 free 了,前一个节点的 next 就是野指针了。指向已经被开释的空间的指针是野指针

这里把 tail 置空,不会把前一个节点的 next 置空
前一个节点是结构体,想改变结构体的内容要用结构体指针

修改1
  1. void SLTPopBack(SLTNode** pphead)
  2. {
  3.     assert(pphead);
  4.     assert(*pphead);
  5.         SLTNode* prev = NULL;
  6.         // 找尾
  7.         SLTNode* tail = *pphead;
  8.         while (tail->next != NULL)
  9.         {
  10.                 prev = tail;
  11.                 tail = tail->next;
  12.         }
  13.         free(tail);
  14.         tail = NULL;
  15.         prev->next = NULL;
  16. }
复制代码

修改2:找的是倒数第2个
  1. void SLTPopBack(SLTNode** pphead)
  2. {
  3.     assert(pphead);
  4.     assert(*pphead);
  5.         // 找尾
  6.         SLTNode* tail = *pphead;
  7.         while (tail->next->next != NULL)
  8.         {
  9.                 tail = tail->next;
  10.         }
  11.         free(tail->next);
  12.         tail->next = NULL;
  13. }
复制代码

如果链表删到只剩1个元素,还删。
如果链表本身为空
  1. void TestSList1()
  2. {
  3.         SLTNode* plist = NULL;
  4.         SLTPushFront(&plist, 1);
  5.         SLTPushFront(&plist, 2);
  6.         SLTPushFront(&plist, 3);
  7.         SLTPushFront(&plist, 4);
  8.         SLTPrint(plist);
  9.         SLTPopBack(&plist);
  10.         SLTPrint(plist);
  11.         SLTPopBack(&plist);
  12.         SLTPrint(plist);
  13.         SLTPopBack(&plist);
  14.         SLTPrint(plist);
  15.         SLTPopBack(&plist);
  16.         SLTPrint(plist);
  17. }
复制代码
              

下面用红圈圈起来的是两组代码在只剩1个的情况下,分别有误的地方

修改:只有1个节点,直接 free,plist 置空。不用找尾节点
以是尾删如果用一级指针接收,phead 是 plist 的拷贝,对 phead 置空的改变不影响 plist,达不到置空 plist 的目标,plist 会酿成野指针

  1. void SLTPopBack(SLTNode** pphead)
  2. {
  3.         //暴力检查
  4.     assert(pphead);
  5.         assert(*pphead);
  6.         //温柔检查
  7.         /*if (*pphead == NULL)
  8.                 return;*/
  9.         if ((*pphead)->next == NULL) // 只有1个节点
  10.         {
  11.                 free(*pphead);
  12.                 *pphead = NULL;
  13.         }
  14.         else // 多个节点
  15.         {
  16.         /*SLTNode* prev = NULL;
  17.         // 找尾
  18.         SLTNode* tail = *pphead;
  19.         while (tail->next != NULL)
  20.         {
  21.                 prev = tail;
  22.                 tail = tail->next;
  23.         }
  24.         free(tail);
  25.         tail = NULL;
  26.         prev->next = NULL;*/
  27.         // 找尾
  28.         SLTNode* tail = *pphead;
  29.         while (tail->next->next != NULL)
  30.         {
  31.                 tail = tail->next;
  32.         }
  33.         free(tail->next);
  34.         tail->next = NULL;
  35.         }
  36. }
复制代码
5.头删

不必要单独处理只有1个节点的情况
  1. void SLTPopFront(SLTNode** pphead)
  2. {
  3.     assert(pphead);
  4.         assert(*pphead);
  5.         SLTNode* first = *pphead;
  6.         *pphead = first->next;
  7.         free(first);
  8.         first = NULL;
  9. }
复制代码
6.查找

  1. SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
  2. {
  3.         SLTNode* cur = phead;
  4.         while (cur)
  5.         {
  6.                 if (cur->data == x)
  7.                 {
  8.                         return cur;
  9.                 }
  10.                 cur = cur->next;
  11.         }
  12.         return NULL;
  13. }
复制代码
返回的是对应节点的指针,可以用 Find 实现修改
  1. void TestSList2()
  2. {
  3.         SLTNode* plist = NULL;
  4.         SLTPushFront(&plist, 1);
  5.         SLTPushFront(&plist, 2);
  6.         SLTPushFront(&plist, 3);
  7.         SLTPushFront(&plist, 4);
  8.         SLTPrint(plist);
  9.         // 值为2的节点 *2
  10.         SLTNode* ret = SLTFind(plist, 2);
  11.         ret->data *= 2;
  12.         SLTPrint(plist);
  13. }
复制代码

Find 重要是与下面的功能相共同
7. Find 查找的功能

我们这里不传下标,传结构体指针,与 C++ 贴合
(1)pos 之前插入

为啥不是在 pos 位置插入? 是把 pos 及以后的数据往后移,以是逻辑上说是之前插入
单链表不得当 pos 之前插入,只得当在后面插入,因为要找到 pos 前一个节点的地址,只能重新找

  1. void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
  2. {
  3.         if (pos == *pphead)
  4.         {
  5.                 SLTPushFront(pphead, x);
  6.         }
  7.         else
  8.         {
  9.                 // 找到 pos 的前一个位置
  10.                 SLTNode* prev = *pphead;
  11.                 while (prev->next != pos)
  12.                 {
  13.                         prev = prev->next;
  14.                 }
  15.                 SLTNode* newnode = BuySLTNode(x);
  16.                 newnode->next = pos;
  17.                 prev->next = newnode;
  18.         }
  19. }
复制代码
如果 pos 不是链表里的指针,while 循环停不下来,终极出现空指针
这种情况怎么办 (以致 pos 就是 NULL)?   
阐明传错了,断言,起码可以排除 NULL
(2)pos 位置删除

这里 *pphead 可以不停言,pos 间接断言了
pos 不为空,有节点,一定不为空链表
pos 位删除,要找到前一个位置。pos 是头,就是头删,先处理这个特殊情况

  1. void SLTErase(SLTNode** pphead, SLTNode* pos)
  2. {
  3.         assert(pphead);
  4.         assert(pos);
  5.         assert(*pphead);
  6.         if (*pphead == pos)
  7.         {
  8.                 SLTPopFront(pphead);
  9.         }
  10.         else
  11.         {
  12.                 // 找到 pos 的前一个位置
  13.                 SLTNode* prev = *pphead;
  14.                 while (prev->next != pos)
  15.                 {
  16.                         prev = prev->next;
  17.                 }
  18.                 prev->next = pos->next;
  19.                 free(pos);
  20.                 // pos = NULL;
  21.         }
  22. }
复制代码
pos = NULL 没用,形参的修改不改变实参。要不要传二级指针呢?不。
为保持和其他的划一性,通常由用的人考虑置空
  1. void TestSList4()
  2. {
  3.         SLTNode* plist = NULL;
  4.         SLTPushFront(&plist, 1);
  5.         SLTPushFront(&plist, 2);
  6.         SLTPushFront(&plist, 3);
  7.         SLTPushFront(&plist, 4);
  8.         SLTPrint(plist);
  9.         SLTNode* ret = SLTFind(plist, 2);
  10.         SLTErase(&plist, ret);
  11.         ret = NULL;
  12.         SLTPrint(plist);
  13. }
复制代码

(3)pos 之后插入

错误写法:会造成死循环
  1. void SLTInsertAfter(SLTNode* pos, SLTDataType x)
  2. {
  3.         assert(pos);
  4.         SLTNode* newnode = BuySLTNode(x);
  5.         pos->next = newnode;
  6.         newnode->next = pos->next;
  7. }
复制代码

精确写法:先改后面
  1. void SLTInsertAfter(SLTNode* pos, SLTDataType x)
  2. {
  3.         assert(pos);
  4.         SLTNode* newnode = BuySLTNode(x);
  5.         newnode->next = pos->next;
  6.         pos->next = newnode;
  7. }
复制代码
(4)pos位置后面删除

法1:pos->next = pos->next->next;  这里从右往左赋值     橙圈的内容丢了,以是要引入del

  1. void SLTEraseAfter(SLTNode* pos)
  2. {
  3.         assert(pos);
  4.         assert(pos->next);
  5.         SLTNode* del = pos->next;
  6.         pos->next = pos->next->next;
  7.         free(del);
  8.         del = NULL;
  9. }
复制代码
法2:好理解
  1. void SLTEraseAfter(SLTNode* pos)
  2. {
  3.         assert(pos);
  4.         assert(pos->next);
  5.         SLTNode* del = pos->next;
  6.         pos->next = del->next;
  7.         free(del);
  8.         del = NULL;
  9. }
复制代码
四.头脑提升

单链表给了 pos 没给头指针
(1)插入

(2)删除
没有前一个位置,就删后一个。先换值,后删。但是不能删尾

五.总结

1.传什么?

我们刚开始拿到链表,plist 是 NULL 。要插入新节点,要让 plist 指向新节点,会改变 plist ,以是要传指针的地址。
删除时,总会删到空,这时要将 plist 置为 NULL ,也改变 plist ,以是也传指针的地址
如果不必要修改头指针的链接,就传一级指针
2.要不要断言?

断言可以排挤显着的错误,制止调试耗时。一定不能为空,就断言
(1)打印、查找

问:是否要 assert 指针 phead 为空? (一级指针)
答:不要。空的 (没有数据) 的链表,顺序表都可以打印、查找。链表为空时,phead == NULL,断言直接停止程序不合适。
顺序表,链表结构不一样,不能一概而论。
phead 是指向第一个存有数据的节点,链表为空时,phead == NULL


顺序表的打印
  1. void SLPrint(SL* ps)
  2. {
  3.         assert(ps);
  4.         for (int i = 0; i < ps->size; ++i)
  5.         {
  6.                 printf("%d ", ps->a[i]);
  7.         }
  8.         printf("\n");
  9. }
复制代码
指针 ps 指向结构体 SL ,顺序表的数据不是存储在结构体上。而是存储在结构体里的一个指针 a 指向的空间。纵然顺序表里没有数据,ps 指向的结构体也是必须要有的。ps->a 是否为空也不重要,到底有没有数据,取决于 ps->size 是否为 0
以是对顺序表而言,指针就不能为空

总结:不要看到指针上来就断言
(2)pphead

要,pphead 不能为空。为什么?
pphead 是 plist 的地址。plist 是指针变量,值有可能是空,地址一定不为空

(3)*pphead

*pphead 就是 plist ,是看是否为空 (二级指针)
要不要断言 *pphead 取决于函数是否包涵空链表的情况
先 assert ( pphead )   后 assert ( *pphead )  如果反了,先 * ,再查抄,有啥用?
空链表能插入,不停言;不能删,要断言。
六.团体代码

SList.h

  1. #pragma once
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <assert.h>
  5. typedef int SLTDataType;
  6. typedef struct SListNode
  7. {
  8.         SLTDataType data;
  9.         struct SListNode* next;
  10. }SLTNode;
  11. void SLTPrint(SLTNode* phead); // 打印链表
  12. void SLTPushBack(SLTNode** pphead, SLTDataType x); // 尾插
  13. void SLTPushFront(SLTNode** pphead, SLTDataType x); // 头插
  14. void SLTPopBack(SLTNode** pphead); // 尾删
  15. void SLTPopFront(SLTNode** pphead); // 头删
  16. SLTNode* SLTFind(SLTNode* phead, SLTDataType x); // 查找
  17. void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x); // pos之前插入
  18. void SLTErase(SLTNode** pphead, SLTNode* pos); // pos位置删除
  19. void SLTInsertAfter(SLTNode* pos, SLTDataType x); // pos之后插入
  20. void SLTEraseAfter(SLTNode* pos); // pos位置后面删除
复制代码
SList.c

  1. #define _CRT_SECURE_NO_WARNINGS 1#include "SList.h"void SLTPrint(SLTNode* phead){        SLTNode* cur = phead;        //while (cur->next != NULL) 错误写法!!!        //while(cur != NULL)        while (cur)        {                printf("%d->", cur->data);                cur = cur->next;                //cur++; 错误写法!!!        }        printf("NULL\n");}// 搞新节点,并初始化SLTNode* BuySLTNode(SLTDataType x){        SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));        if (newnode == NULL)        {                perror("malloc fail");                return NULL;        }        // 初始化        newnode->data = x;        newnode->next = NULL;        return newnode;}void SLTPushBack(SLTNode** pphead, SLTDataType x)
  2. {
  3.         assert(pphead);
  4.         SLTNode* newnode = BuySLTNode(x);
  5.         if (*pphead == NULL)
  6.         {
  7.                 *pphead = newnode;
  8.         }
  9.         else
  10.         {
  11.                 // 找尾
  12.                 SLTNode* tail = *pphead;
  13.                 while (tail->next != NULL)
  14.                 {
  15.                         tail = tail->next;
  16.                 }
  17.                 tail->next = newnode;
  18.         }
  19. }void SLTPushFront(SLTNode** pphead, SLTDataType x)
  20. {
  21.         assert(pphead);
  22.         SLTNode* newnode = BuySLTNode(x);
  23.         newnode->next = *pphead;
  24.         *pphead = newnode;
  25. }void SLTPopBack(SLTNode** pphead){        //暴力查抄        assert(pphead);        assert(*pphead);        //温柔查抄        /*if (*pphead == NULL)                return;*/        if ((*pphead)->next == NULL) // 只有1个节点        {                free(*pphead);                *pphead = NULL;        }        else // 多个节点        {        /*SLTNode* prev = NULL;        // 找尾        SLTNode* tail = *pphead;        while (tail->next != NULL)        {                prev = tail;                tail = tail->next;        }        free(tail);        tail = NULL;        prev->next = NULL;*/        // 找尾        SLTNode* tail = *pphead;        while (tail->next->next != NULL)        {                tail = tail->next;        }        free(tail->next);        tail->next = NULL;        }}void SLTPopFront(SLTNode** pphead){        assert(pphead);        assert(*pphead);        SLTNode* first = *pphead;        *pphead = first->next;        free(first);        first = NULL;}SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
  26. {
  27.         SLTNode* cur = phead;
  28.         while (cur)
  29.         {
  30.                 if (cur->data == x)
  31.                 {
  32.                         return cur;
  33.                 }
  34.                 cur = cur->next;
  35.         }
  36.         return NULL;
  37. }void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x){        assert(pphead);        assert(pos);        if (pos == *pphead)        {                SLTPushFront(pphead, x);        }        else        {                // 找到 pos 的前一个位置                SLTNode* prev = *pphead;                while (prev->next != pos)                {                        prev = prev->next;                }                SLTNode* newnode = BuySLTNode(x);                newnode->next = pos;                prev->next = newnode;        }}void SLTErase(SLTNode** pphead, SLTNode* pos)
  38. {
  39.         assert(pphead);
  40.         assert(pos);
  41.         assert(*pphead);
  42.         if (*pphead == pos)
  43.         {
  44.                 SLTPopFront(pphead);
  45.         }
  46.         else
  47.         {
  48.                 // 找到 pos 的前一个位置
  49.                 SLTNode* prev = *pphead;
  50.                 while (prev->next != pos)
  51.                 {
  52.                         prev = prev->next;
  53.                 }
  54.                 prev->next = pos->next;
  55.                 free(pos);
  56.                 // pos = NULL;
  57.         }
  58. }void SLTInsertAfter(SLTNode* pos, SLTDataType x)
  59. {
  60.         assert(pos);
  61.         SLTNode* newnode = BuySLTNode(x);
  62.         newnode->next = pos->next;
  63.         pos->next = newnode;
  64. }void SLTEraseAfter(SLTNode* pos){        assert(pos);        assert(pos->next);        //SLTNode* del = pos->next;        //pos->next = pos->next->next;        //free(del);        //del = NULL;        SLTNode* del = pos->next;        pos->next = del->next;        free(del);        del = NULL;}
复制代码

本篇的分享就到这里了,感谢观看,如果对你有帮助,别忘了点赞+收藏+关注
小编会以本身学习过程中遇到的题目为素材,一连为您推送文章

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

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

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

魏晓东

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表