目录
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
这张图生动形象地呈现了链表的结构。
主要有两种类型的链表:单向链表和双向链表。在 单向链表中,每个节点包含一个数据元素和一个指向下一个节点的引用。而在 双向链表中,每个节点有两个引用,一个指向前一个节点,另一个指向后一个节点。
本次讲解基础的单向链表。
我们创建三个文件:
- 头文件LList.h用于调用库函数、声明结构体和函数。
- 源文件LList.c存储函数。
- 源文件text.c进行测试。
每个源文件都必须包含LList.h。
- #include
-
- typedef int SLTDataType;
- typedef struct SListNode
- {
- SLTDataType data;
- struct SListNode* next;
- }SLTNode;
SLTDataType。
- void SLTPrint(SLTNode* phead)
- {
- SLTNode* cur = phead;
- while (cur != NULL) {
- printf("%d->", cur->data);
- cur = cur->next;
- }
- printf("NULL\n");
- }
- void SLPushFront(SLTNode** pphead, SLTDataType x)
- {
- assert(pphead);
- SLTNode* newnode = BuyLTNode(x);
- newnode->next = *pphead;
- *pphead = newnode;
- }
接下来讲解为新节点开辟空间的函数 BuyLTNode
- SLTNode* BuyLTNode(SLTDataType x)
- {
- SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
- if (newnode == NULL) {
- perror("malloc fall");
- return;
- }
- newnode->data = x;
- newnode->next = NULL;
- return newnode;
- }
- void SLPushBack(SLTNode** pphead, SLTDataType x)
- {
- assert(pphead);
- SLTNode* newnode = BuyLTNode(x);
- if (*pphead == NULL){
- *pphead = newnode;
- }
- else {
- SLTNode* tail = *pphead;
- while (tail->next != NULL)
- tail = tail->next;
-
- tail->next = newnode;
- }
- }
*pphead
指向新节点 newnode
,使其成为新的链表的头节点。- void SLPopFront(SLTNode** pphead)
- {
- assert(pphead);
- assert(*pphead);
- SLTNode* del = *pphead;
- *pphead = (*pphead)->next;
- free(del);
- }
- void SLPopBack(SLTNode** pphead)
- {
- assert(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;
- }
- }
- SLTNode* STFind(SLTNode* phead, SLTDataType x)
- {
- SLTNode* cur = phead;
- while (cur) {
- if (cur->data == x)
- return cur;
-
- cur = cur->next;
- }
- return NULL;
- }
x
的节点。cur,否则返回值为空。
-
- void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
- {
- assert(pphead);
- assert(pos);
- if (*pphead == pos){
- SLPushFront(pphead, x);
- }
- else {
- SLTNode* prev = *pphead;
- while (prev->next != pos){
- prev = prev->next;
- }
-
- SLTNode* newnode = BuyLTNode(x);
- prev->next = newnode;
- newnode->next = pos;
- }
- }
prev
,然后使用循环找到要插入位置 pos
前面的节点。创建一个新的节点 newnode
并将数据值 x
存储在其中。
修改 prev
节点的 next
指针,使其指向新节点 newnode
,从而将新节点插入到 pos
前面。
- void SLInsertAfter(SLTNode* pos, SLTDataType x)
- {
- assert(pos);
- SLTNode* newnode = BuyLTNode(x);
- newnode->next = pos->next;
- pos->next = newnode;
- }
newnode
并将数据值 x
存储在其中。- void SLErase(SLTNode** pphead, SLTNode* pos)
- {
- assert(pphead);
- assert(*pphead);
- if (pos == *pphead){
- SLPopFront(pphead);
- }
- else {
- SLTNode* prev = *pphead;
- while (prev->next != pos) {
- prev = prev -> next;
- }
- prev->next = pos->next;
- free(pos);
- }
- }
- void SLEraseAfter(SLTNode* pos)
- {
- assert(pos);
- assert(pos->next);
- SLTNode* next = pos->next;
- pos->next = next->next;
- free(next);
- }
pos
后面节点的指针 next
,以便稍后释放该节点的内存。修改 pos
节点的 next
指针,将其指向 next
的下一个节点,从而绕过要删除的节点,使链表不再包含它。
最后,使用 free
函数释放 next
指向的节点的内存,完成删除操作。
- void SListDestroy(SLTNode* pphead)
- {
- SLTNode* cur = pphead;
- SLTNode* tmp = NULL;
- while (cur != NULL) {
- tmp = cur;
- cur = cur->next;
- free(tmp);
- }
- }
cur
和 tmp
,用于遍历链表并释放内存。开始时,cur
被初始化为链表的头节点指针 pphead
。这是一个循环,它会一直执行,直到 cur
变为 NULL
,也就是遍历到链表的末尾。
在循环中,首先将 cur
赋值给 tmp
,以便稍后释放 cur
指向的节点的内存。
然后,将 cur
移动到下一个节点,即 cur = cur->next;
最后,使用 free
函数释放 tmp
指向的节点的内存,即释放链表中的一个节点,接着进行循环依次释放节点直到链表最后。
- #include
- #include
- #include
-
- typedef int SLTDataType;
- typedef struct SListNode
- {
- SLTDataType data;
- struct SListNode* next;
- }SLTNode;
-
- //打印链表
- void SLTPrint(SLTNode* phead);
-
- //头插尾插
- void SLPushFront(SLTNode** pphead, SLTDataType x);
- void SLPushBack(SLTNode** pphead, SLTDataType x);
-
- //头删尾删
- void SLPopFront(SLTNode** pphead);
- void SLPopBack(SLTNode** pphead);
-
- // 单链表查找
- SLTNode * STFind(SLTNode * phead, SLTDataType x);
-
- // 在pos之前插入
- void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
- void SLInsertAfter(SLTNode* pos, SLTDataType x);
-
- // 删除pos位置的值
- void SLErase(SLTNode** pphead, SLTNode* pos);
-
- // 删除pos位置后面的值
- void SLEraseAfter(SLTNode* pos);
-
- // 单链表的销毁
- void SListDestroy(SLTNode* plist);
- #define _CRT_SECURE_NO_WARNINGS 1
- #include "LList.h"
-
- void SLTPrint(SLTNode* phead)
- {
- SLTNode* cur = phead;
- while (cur != NULL) {
- printf("%d->", cur->data);
- cur = cur->next;
- }
- printf("NULL\n");
- }
-
- SLTNode* BuyLTNode(SLTDataType x)//为新元素开辟空间
- {
- SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
- if (newnode == NULL) {
- perror("malloc fall");
- return;
- }
- newnode->data = x;
- newnode->next = NULL;
- return newnode;
- }
-
- void SLPushFront(SLTNode** pphead, SLTDataType x)//头插
- {
- assert(pphead); // 链表为空,pphead也不为空,因为他是头指针plist的地址
- //assert(*pphead); // 不能断言,链表为空,也需要能插入
- SLTNode* newnode = BuyLTNode(x);//newnode是局部变量
- newnode->next = *pphead;//头插后首节点next指向原有的首节点
- *pphead = newnode;//将链表的头指针 *pphead 指向新插入的节点
- }
-
- void SLPushBack(SLTNode** pphead, SLTDataType x)
- {
- assert(pphead); // 链表为空,pphead也不为空,因为他是头指针plist的地址
- SLTNode* newnode = BuyLTNode(x);
- //两种情况
- //空链表 非空链表
- if (*pphead == NULL)//链表为空改变结构体指针
- *pphead = newnode;
-
- else {//不为空,则改变结构体的节点
- SLTNode* tail = *pphead;
- while (tail->next != NULL)
- tail = tail->next;
-
- tail->next = newnode;
- }
- }
-
- void SLPopFront(SLTNode** pphead)
- {
- assert(pphead); // 链表为空,pphead也不为空,因为他是头指针plist的地址
- assert(*pphead); // 链表为空,不能头删。(当然你还可以用温柔的检查)
- SLTNode* del = *pphead;//指针del用于释放节点空间
- *pphead = (*pphead)->next;
- free(del);
- }
-
- void SLPopBack(SLTNode** pphead)
- {
- assert(pphead); // 链表为空,pphead也不为空,因为他是头指针plist的地址
- assert(*pphead); // 链表为空,不能头删。(当然你还可以用温柔的检查)
-
- //只有一个节点
- if ((*pphead)->next == NULL) {
- free(*pphead);
- *pphead = NULL;//修改头节点为空
- }
- else {
- //第一种增加前项变量
- //SLTNode* prev = NULL;
- //SLTNode* tail = *pphead;
- //while (tail->next) {
- // prev = tail;
- // tail = tail->next;
- //}
- //free(tail);
- //prev->next = NULL;
-
- //第二种不新增变量
- //改变结构体的节点
- SLTNode* tail = *pphead;
- while (tail->next->next) {
- tail = tail->next;
- }
- free(tail->next);//将指向的最后一个节点释放
- tail->next = NULL;
- }
- }
-
-
- SLTNode* STFind(SLTNode* phead, SLTDataType x)//找到返回链表地址
- {
- SLTNode* cur = phead;
- while (cur) {
- if (cur->data == x)
- return cur;
-
- cur = cur->next;
- }
- return NULL;
- }
-
- // 在pos之前插入
- void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
- {
- assert(pphead);
- assert(pos);
- if (*pphead == pos){//在头节点前插入等于头插
- SLPushFront(pphead, x);
- }
- else {
- SLTNode* prev = *pphead;//用于找到pos前的位置
-
- while (prev->next != pos){
- prev = prev->next;
- }
-
- SLTNode* newnode = BuyLTNode(x);
- prev->next = newnode;//pos前一个位置next指向新开辟节点
- newnode->next = pos;//新节点next指向pos
- }
- }
-
- // 在pos之后插入
- void SLInsertAfter(SLTNode* pos, SLTDataType x)
- {
- assert(pos);
- SLTNode* newnode = BuyLTNode(x);
- //下面两行不能调换顺序,否则无法链接新节点后项节点
- newnode->next = pos->next;
- pos->next = newnode;
- }
-
- void SLErase(SLTNode** pphead, SLTNode* pos)// 删除pos位置的值
- {
- assert(pphead);
- assert(*pphead);//链表为空则不能删除
- if (pos == *pphead){
- SLPopFront(pphead);
- }
- else {
- SLTNode* prev = *pphead;
- while (prev->next != pos) {//找到pos前一个节点
- prev = prev -> next;
- }
- prev->next = pos->next;//将pos前一个节点的next指向pos后一个节点
- free(pos);//释放pos空间
- }
- }
-
- void SLEraseAfter(SLTNode* pos)
- {
- assert(pos);
- assert(pos->next);//后项为空则不能删除
- SLTNode* next = pos->next;
- pos->next = next->next;
- free(next);
- }
-
- void SListDestroy(SLTNode* pphead)
- {
- SLTNode* cur = pphead;
- SLTNode* tmp = NULL;
- while (cur != NULL) {
- tmp = cur;
- cur = cur->next;
- free(tmp);
- }
- }
- #define _CRT_SECURE_NO_WARNINGS 1
-
- #include "LList.h"
-
- void test1()
- {
- SLTNode* plist = NULL;
- SLPushFront(&plist, 5);
- SLPushFront(&plist, 4);
- SLPushFront(&plist, 3);
- SLPushBack(&plist, 6);
-
- //SLPopFront(&plist);
-
- SLTNode* pos = STFind(plist, 3);
- SLInsert(&plist, pos, 99);
-
- //pos = STFind(plist, 2);
- //if (pos)
- //{
- // SLInsertAfter(pos, 20);
- //}
-
- //SLPopBack(&plist);
- //SLPopBack(&plist);
- //SLPopBack(&plist);
- //SLPopBack(&plist);
- //SLPopBack(&plist);
-
- SLTPrint(plist);
- }
-
- int main()
- {
- test1();
- return 0;
- }