目录
前言
编译环境:VS2022
编程语言:C
内存管理(动态/静态):动态内存管理
是否带哨兵位:否
链表类型:单链表
代码分析
数据类型
typedef int SLTDateType; typedef struct SListNode { SLTDateType data; //数据域 struct SListNode* next; //指针域 }SListNode;
动态申请一个结点
//申请后返回该结点的地址,所以函数的类型为结构体指针类型 SListNode* //参数为结构体成员 数据域的类型 SLTDateType,作为新结点的数据域 SListNode* BuySListNode(SLTDateType x) { //动态内存开辟 SListNode* newnode = (SListNode*)malloc(sizeof(SListNode)); //检查申请结点是否成功 if (newnode == NULL) { perror("malloc"); return; } newnode->data = x; newnode->next = NULL; return newnode; }
单链表打印
void SListPrint(SListNode* plist) { //此处需要进行断言吗? //无需断言,空的链表也是可以打印的 //拷贝头指针,防止我们需要再次用到头指针的时候找不到了 SListNode* cur = plist; while (cur != NULL) { printf("%d->", cur->data); //每输出一个结点的数据域,指针就往后走一步 cur = cur->next; } printf("NULL\n"); }
单链表的尾插
1.函数第一个参数传一级指针还是二级指针?为什么? 2.断言 pplist 还是 *pplist ?为什么? 3.找尾结点是判断 tail==NULL 还是 tail->next==NULL void SListPushBack(SListNode** pplist, SLTDateType x) { //断言 assert(pplist); //创建新节点 SListNode* newnode = BuySListNode(x); //判断链表是否为空 if ((*pplist)->next == NULL) { *pplist = newnode; } else { SListNode* tail = *pplist; //找尾结点 while (tail->next != NULL) { tail = tail->next; } tail->next = newnode; } } // 1.二级指针,要想改变结构体指针,就需要传结构体指针的地址 2.断言 pplist 因为即使链表为空,指向链表的头指针也不为空 如果断言 *pplist 会导致链表为空的时候无法进行尾插 3.找尾结点判断 tail->next==NULL 如果tail已经为空了,我们就没有办法找到指向tail的那个结点了
单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x) { assert(pplist);//链表为空,pplist也不为空,pplist是头指针的地址 //assert(*pplist);//不能断言,链表为空,也需要能插入 SListNode* newnode = BuySListNode(x); newnode->next = *pplist; *pplist = newnode; } 注意: pplist 指向头结点的地址(表示头结点的地址的地址) *pplist 指向头结点(表示头结点的地址)
单链表的尾删
void SListPopBack(SListNode** pplist) { //需要断言吗?可以断言吗? assert(pplist); //需要断言吗?可以断言吗? assert(*pplist); //链表为空 if ((*pplist)->next == NULL) { free(*pplist); *pplist = NULL; } //链表非空 else { //找尾 SListNode* tail = *pplist; //while循环的结束条件是 (tail->next) 还是 (tail->next->next) ? while (tail->next->next) { tail = tail->next; } free(tail->next); tail->next = NULL; } } 必须断言 assert(pplist); //即使链表为空,pplist也不为空 可以断言(也可以不断言,如果不断言就需要用if语句判断链表是否为空) assert(*pplist); //链表为空,无需进行删除 while循环的结束条件必须是 (tail->next->next) 如果循环结束为 (tail->next),循环结束时 tail->next = NULL 虽然我们可以成功删除尾结点,但是上一结点的next又指向了哪里呢? 所以我们需要找到尾结点的上一结点,释放尾结点,并将尾结点的上一结点的next置为NULL
单链表的头删
void SListPopFront(SListNode** pplist) { //需要断言吗? assert(pplist);//必须断言 //需要断言吗? assert(*pplist);//可以断言 // SListNode* del = *pplist; *pplist = (*pplist)->next; free(del); }
单链表查找
我们可以根据需要返回的数据类型调整函数类型 此处返回一个结点的地址,所以函数类型为 SListNode* 此处传参为一级指针,没有必要使用二级指针(虽然也可以使用) SListNode* SListFind(SListNode* plist, SLTDateType x) { //assert(plist);无需断言 SListNode* cur = plist; while (cur != NULL) { if (cur->data == x) { return cur; } cur = cur->next; } return NULL; }
代码并不是很难懂,就不作图说明了
单链表在pos位置插入x
void SListInsert(SListNode** pplist, SListNode* pos, SLTDateType x) { assert(pplist); assert(pos);//判断插入的位置是否非法 //头插 if (*pplist == pos) { SListPushFront(pplist, x); } else { SListNode* prev = *pplist; //找到pos的前一个结点 while (prev->next != pos) { prev = prev->next; } SListNode* newcode = BuySListNode(x); prev->next = newcode; newcode->next = pos; } }
单链表在pos位置之后插入x
如果限制参数的个数,我们也可以选择这样的插入方式 void SListInsertAfter(SListNode* pos, SLTDateType x) { assert(pos); SListNode* newcode = BuySListNode(x); newcode->next = pos->next; pos->next = newcode; //上述两个语句是否可以交换顺序? //不能,如果先执行第二条语句,链表会找不到pos后面的结点了 }
单链表删除pos位置的值
void SListErase(SListNode** pplist, SListNode* pos) { assert(pplist); assert(pos); //头删 if (*pplist == pos) { SListPopFront(pplist); } else { SListNode* prev = *pplist; while (prev->next != NULL) { prev = prev->next; } prev->next = pos->next; free(pos); } }
此处删除操作与插入操作类似,都是先查找,再进行 删除/插入
单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos) { assert(pos); assert(pos->next); SListNode* next = pos->next; pos->next = next->next; free(next); }
此处删除操作与插入操作类似,都是借用一个额外的结点直接对pos位置的结点进行处理
单链表的销毁
两种单链表的销毁方式都可以 //void SListDestroy(SListNode** pplist) //{ // assert(*pplist); // SListNode *cur = *pplist; // while (cur) // { // SListNode* next = cur->next; // free(cur); // cur = next; // } // *pplist = NULL; //} void SListDestroy(SListNode* plist) { SListNode* cur = plist; //遍历单链表 while (cur) { SListNode* next = cur->next; free(cur); cur = next; } //记得置空头结点 plist = NULL; }
完整代码
slist.h
#pragma once #include <stdio.h> #include <stdlib.h> #include <assert.h> // slist.h typedef int SLTDateType; typedef struct SListNode { SLTDateType data; struct SListNode* next; }SListNode; // 动态申请一个节点 SListNode* BuySListNode(SLTDateType x); // 单链表打印 void SListPrint(SListNode* plist); // 单链表尾插 void SListPushBack(SListNode** pplist, SLTDateType x); // 单链表的头插 void SListPushFront(SListNode** pplist, SLTDateType x); // 单链表的尾删 void SListPopBack(SListNode** pplist); // 单链表头删 void SListPopFront(SListNode** pplist); // 单链表查找 SListNode* SListFind(SListNode* plist, SLTDateType x); // 单链表在pos位置插入x void SListInsert(SListNode** pplist, SListNode* pos, SLTDateType x); // 单链表在pos位置之后插入x // 分析思考为什么不在pos位置之前插入? void SListInsertAfter(SListNode* pos, SLTDateType x); // 单链表删除pos位置的值 void SListErase(SListNode** pplist, SListNode* pos); // 单链表删除pos位置之后的值 // 分析思考为什么不删除pos位置? void SListEraseAfter(SListNode* pos); // 单链表的销毁 //void SListDestroy(SListNode** pplist); void SListDestroy(SListNode* plist);
slist.c
#include "slist.h" SListNode* BuySListNode(SLTDateType x) { SListNode* newnode = (SListNode*)malloc(sizeof(SListNode)); if (newnode == NULL) { perror("malloc"); return; } newnode->data = x; newnode->next = NULL; return newnode; } void SListPrint(SListNode* plist) { //无需断言 SListNode* cur = plist; while (cur != NULL) { printf("%d->", cur->data); cur = cur->next; } printf("NULL\n"); } void SListPushBack(SListNode** pplist, SLTDateType x) { assert(pplist); // SListNode* newnode = BuySListNode(x); if ((*pplist)->next == NULL) { *pplist = newnode; } else { SListNode* tail = *pplist; while (tail->next != NULL) { tail = tail->next; } tail->next = newnode; } } void SListPushFront(SListNode** pplist, SLTDateType x) { assert(pplist);//链表为空,pplist也不为空,pplist是头指针的地址 //assert(*pplist);//不能断言,链表为空,也需要能插入 SListNode* newnode = BuySListNode(x); newnode->next = *pplist; *pplist = newnode; } void SListPopBack(SListNode** pplist) { assert(pplist); assert(*pplist); // if ((*pplist)->next == NULL) { free(*pplist); *pplist = NULL; } // else { SListNode* tail = *pplist; while (tail->next->next) { tail = tail->next; } free(tail->next); tail->next = NULL; } } void SListPopFront(SListNode** pplist) { assert(pplist); assert(*pplist); SListNode* del = *pplist; *pplist = (*pplist)->next; free(del); } SListNode* SListFind(SListNode* plist, SLTDateType x) { //assert(plist);无需断言 SListNode* cur = plist; while (cur != NULL) { if (cur->data == x) { return cur; } cur = cur->next; } return NULL; } void SListInsert(SListNode** pplist, SListNode* pos, SLTDateType x) { assert(pplist); assert(pos); if (*pplist == pos) { SListPushFront(pplist, x); } else { SListNode* prev = *pplist; while (prev->next != pos) { prev = prev->next; } SListNode* newcode = BuySListNode(x); prev->next = newcode; newcode->next = pos; } } //在pos之前插入 void SListInsertAfter(SListNode* pos, SLTDateType x) { assert(pos); SListNode* newcode = BuySListNode(x); newcode->next = pos->next; pos->next = newcode; } void SListErase(SListNode** pplist, SListNode* pos) { assert(pplist); assert(pos); if (*pplist == pos) { SListPopFront(pplist); } else { SListNode* prev = *pplist; while (prev->next != NULL) { prev = prev->next; } prev->next = pos->next; free(pos); } } void SListEraseAfter(SListNode* pos) { assert(pos); assert(pos->next); SListNode* next = pos->next; pos->next = next->next; free(next); } //void SListDestroy(SListNode** pplist) //{ // assert(*pplist); // SListNode *cur = *pplist; // while (cur) // { // SListNode* next = cur->next; // free(cur); // cur = next; // } // *pplist = NULL; //} void SListDestroy(SListNode* plist) { SListNode* cur = plist; while (cur) { SListNode* next = cur->next; free(cur); cur = next; } plist = NULL; }
test.c
#include "slist.h" void test1() { SListNode* plist = NULL; SListPushFront(&plist, 1); SListPushFront(&plist, 2); SListPushFront(&plist, 3); SListPushBack(&plist, 4); SListPushBack(&plist, 5); SListPushBack(&plist, 6); SListFind(plist, 6); SListPrint(plist); SListPopFront(&plist); SListPopBack(&plist); SListFind(plist, 6); SListPrint(plist); SListNode* pos; pos = SListFind(plist, 2); if (pos) { //SListInsert(&plist, pos, 10); SListInsertAfter(pos, 20); } SListPrint(plist); pos = SListFind(plist, 4); if (pos) { //SListErase(&plist, pos); SListEraseAfter(pos); } SListPrint(plist); SListDestroy(plist); //SListPrint(plist); } int main() { test1(); return 0; }