不同于顺序表,顺序表的数据是存储在一个连续的空间里的
而链表它是链接起来的结构体地址。


所以我们不用像顺序表一样先创建一块空间出来,而是创建一个能存数据节点和节点与下一个节点之间的连接;
所以:“一个能存数据节点”我们用int Data表示;
“与下一个节点之间的连接”:我们用指针连接起来。

为了方便测试,我们先写个打印单链表的函数;

1.这个打印函数需要断言吗?
不需要,即使结构体为空,也能打印,只不过是没有数据而已,这时打印出来的就是空的。如果能打印出来,但是却断言报错,不太合适。
2.怎么打印?
用一个指针cur指向结构体,用while循环打印出来,当cur指向的结构体为空时,停止打印。
3.while的判断条件可以是while(cur->next)吗?
不可以,因为这样最后一个的数据就打印不出来了。
4.在while循环中,让cur指向下一个结构体,可以用cur++吗?
不可以,不同于顺序表,顺序表的数据是存储在一个连续的空间里的,链表它是链接起来的结构体地址,是节点与节点之间的连接,cur++无法指向下一个节点。
- void SLTPrint(SLTNode* phead)
- {
- SLTNode* cur = phead;
- while (cur)
- {
- printf("%d->", cur->Data);
- cur = cur->next;
- }
- printf("NULL\n");
- }
每在插入一个数据之前,首先得为这个结构体开辟一个结点,
用malloc开辟,由于插入数据时我们都要进行开辟一个结点的操作,所以我们可以打包成一个函数:

进行尾插首先就是要找到尾节点,
找尾分两种情况:
1. 当*pphead本身为空时,就直接将newnode插入就可以了;
2. *pphead本身不为空时,只要找到tail->next为空的,那个就是结构体的尾了


当pphead不为空时,找尾while循环的判断条件可以写成这样tail!=NULL与插入结点时tail=newnode吗?
不可以,因为这样就无法保持链接状态了
尾插的本质是:原为节点中存储新的尾节点的地址。
- SLTNode* SLTNewnode(SLTDataType x)
- {
- SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
- if (newnode == NULL)
- {
- perror("melloc fail");
- return NULL;
- }
- newnode->Data = x;
- newnode->next = NULL;
-
- return newnode;
-
- }
-
- void SLTPushBack(SLTNode** pphead, SLTDataType x)
- {
- //创建节点
- SLTNode* newnode = SLTNewnode(x);
- if (newnode == NULL)
- {
- return;
- }
-
- //情况一:pphead为空
- if (*pphead == NULL)
- {
- *pphead = newnode;
- }
- //情况二:pphead不为空
- else
- {
- //找到尾节点
- SLTNode* tail = *pphead;
- while (tail->next)
- {
- tail = tail->next;
- }
-
- tail->next = newnode;
- }
- }
1.尾删需要断言吗?
需要,因为如果链表为空,是删不了的;
2.尾删的思路
尾删分三种讨论:
1.如果链表为空,删不了,我们这里断言判断一下
2.链表中只有一个数据
3.链表中的数据为一个以上


- void SLTPopBack(SLTNode** pphead)
- {
- assert(*pphead);
-
- //只有一个节点
- if ((*pphead)->next == NULL)
- {
- free(*pphead);
- *pphead = NULL;
- }
- else
- {
- //找尾置空
- SLTNode* tail = *pphead;
- while (tail->next->next)
- {
- tail = tail->next;
- }
- free(tail->next);
- tail->next = NULL;
- }
-
- }
1.头插需要断言吗?
但是当链表为空的时候,可以插入数据,*pphead是不需要断言的。
2.头插的思路
首先先要创建一个结点,将结点的next与链表的第一个指针链接起来。最后要注意把链表的头给改成newnode。


- void SLTPushFront(SLTNode** pphead, SLTDataType x)
- {
- SLTNode* newnode = SLTNewnode(x);
- if (newnode == NULL)
- {
- return;
- }
-
-
- newnode->next = *pphead;
- *pphead = newnode;
- }
1.头删需要断言吗?
空链表不能删除,所以*pphead也需要断言。
头删的思路:


- void SLTPopFront(SLTNode** pphead)
- {
- assert(*pphead);
-
-
- SLTNode* head = *pphead;
- *pphead = head->next;
- free(head);
- head = NULL;
-
- }
1.查找需要断言吗?
不需要,链表为空就返回NULL;
2.查找的思路
当链表的cur不为空,就继续逐一比对,找到了就返回cur,没找到就指向下一个,直到cur指向空;
如果还没找到,就返回NULL;

这里的phead用一级指针就可以了,因为不用修改里面的任何值;
- SLTNode* SListFind(SLTNode* phead, SLTDataType x)
- {
- SLTNode* cur = phead;
- while (cur)
- {
- if (cur->Data == x)
- {
- return cur;
- }
- cur = cur->next;
- }
- //如果没找到就返回NULL
- return NULL;
- }
1.需要断言吗?
指定的位置pos不能为空,所以需要断言;
2.思路
创建一个新结点newnode,然后先让newnode->next = pos->next;让newnode的后面链接起来,在让pos和newnode链接起来pos->next = newnode;;
反过来写的话,由于pos->next已经被改了,所以不能是pos与newnode链接,插入就会失败;

-
- void SLTInsertAfter( SLTNode* pos, SLTDataType x)
- {
- assert(pos);
-
-
- SLTNode* newnode = SLTNewnode(x);
- if (newnode == NULL)
- {
- return;
- }
-
- newnode->next = pos->next;
- pos->next = newnode;
-
- }
1.需要断言吗?
指定的位置pos不能为空,所以需要断言;

- void SListEraseAfter( SLTNode* pos)
- {
- assert(pos);
-
- if (pos->next == NULL)
- {
- printf("已是最后一个,不能删\n");
- return;
- }
-
- SLTNode* cur = pos->next;
- pos->next = pos->next->next;
- free(cur);
- cur= NULL;
- }
思路
结点逐一free,最后记得把*pphead改为最后的cur。

- void SLTDel(SLTNode** pphead)
- {
- assert(*pphead);
-
- SLTNode* cur = *pphead;
- SLTNode* prev = cur;
- while (cur)
- {
- prev = cur->next;
- free(cur);
- cur = prev;
- }
- *pphead = cur;
- }
为什么不像顺序表一样写初始化函数?
可写可不写,这里结构体里面的数据比较少,我们在测试代码的时候直接用指针指向了一块空间。

为什么要传二级指针?
想要改变变量的数值而不会因为栈帧的销毁而销毁,就得取到该变量的地址;
什么时候要传二级指针,什么时候要传一级指针?
想要改变变量里的数值就要传址调用,二级指针用来接收一级指针;
如果只是简单的访问就用传值调用;
- #define _CRT_SECURE_NO_WARNINGS 1
-
- #include
- #include
- #include
-
-
- typedef int SLTDataType;
-
- typedef struct SLTlist
- {
- SLTDataType Data;
- struct SLTlist * next;
-
- }SLTNode;
-
- void SLTPrint(SLTNode* phead);
- void SLTPushBack(SLTNode** pphead, SLTDataType x);
- void SLTPopBack(SLTNode** pphead);
- void SLTPushFront(SLTNode** pphead, SLTDataType x);
- void SLTPopFront(SLTNode** pphead);
- SLTNode* SListFind(SLTNode* phead, SLTDataType x);
- void SLTInsertAfter( SLTNode* pos, SLTDataType x);
- void SListEraseAfter( SLTNode* pos);
- void SLTDel(SLTNode** pphead);
- #include"SLlist.h"
-
- void SLTPrint(SLTNode* phead)
- {
- SLTNode* cur = phead;
- while (cur)
- {
- printf("%d->", cur->Data);
- cur = cur->next;
- }
- printf("NULL\n");
- }
-
- SLTNode* SLTNewnode(SLTDataType x)
- {
- SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
- if (newnode == NULL)
- {
- perror("melloc fail");
- return NULL;
- }
- newnode->Data = x;
- newnode->next = NULL;
-
- return newnode;
-
- }
-
- void SLTPushBack(SLTNode** pphead, SLTDataType x)
- {
- //创建节点
- SLTNode* newnode = SLTNewnode(x);
- if (newnode == NULL)
- {
- return;
- }
-
- //情况一:pphead为空
- if (*pphead == NULL)
- {
- *pphead = newnode;
- }
- //情况二:pphead不为空
- else
- {
- //找到尾节点
- SLTNode* tail = *pphead;
- while (tail->next)
- {
- tail = tail->next;
- }
-
- tail->next = newnode;
- }
- }
-
- void SLTPopBack(SLTNode** pphead)
- {
- assert(*pphead);
-
- //只有一个节点
- if ((*pphead)->next == NULL)
- {
- free(*pphead);
- *pphead = NULL;
- }
- else
- {
- //找尾置空
- SLTNode* tail = *pphead;
- while (tail->next->next)
- {
- tail = tail->next;
- }
- free(tail->next);
- tail->next = NULL;
- }
-
- }
-
- void SLTPushFront(SLTNode** pphead, SLTDataType x)
- {
- SLTNode* newnode = SLTNewnode(x);
- if (newnode == NULL)
- {
- return;
- }
-
-
- newnode->next = *pphead;
- *pphead = newnode;
- }
-
- void SLTPopFront(SLTNode** pphead)
- {
- assert(*pphead);
-
-
- SLTNode* head = *pphead;
- *pphead = head->next;
- free(head);
- head = NULL;
-
- }
-
- SLTNode* SListFind(SLTNode* phead, SLTDataType x)
- {
- SLTNode* cur = phead;
- while (cur)
- {
- if (cur->Data == x)
- {
- return cur;
- }
- cur = cur->next;
- }
- //如果没找到就返回NULL
- return NULL;
- }
-
- void SLTInsertAfter( SLTNode* pos, SLTDataType x)
- {
- assert(pos);
-
-
- SLTNode* newnode = SLTNewnode(x);
- if (newnode == NULL)
- {
- return;
- }
-
- newnode->next = pos->next;
- pos->next = newnode;
-
- }
-
- void SListEraseAfter( SLTNode* pos)
- {
- assert(pos);
-
- if (pos->next == NULL)
- {
- printf("已是最后一个,不能删\n");
- return;
- }
-
- SLTNode* cur = pos->next;
- pos->next = pos->next->next;
- free(cur);
- cur= NULL;
- }
-
- void SLTDel(SLTNode** pphead)
- {
- assert(*pphead);
-
- SLTNode* cur = *pphead;
- SLTNode* prev = cur;
- while (cur)
- {
- prev = cur->next;
- free(cur);
- cur = prev;
- }
- *pphead = cur;
- }