赞
踩
目录
单链表是一种在物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接顺序实现的。
链表的每个结点有两个部分,分别是数据和指向下个结点的指针,每个链表的最后一个结点的下一个结点为NULL(不能对NULL解引用)。
放一张bit课件里的图,我觉得很形象:
- typedef int SLDataType;//重定义一下在链表内存放的数据类型,方便后期对类型进行统一修改
-
- //链表的单个结点
- typedef struct SListNode {//Single List Node :链表结点
- SLDataType data;//存放的数据
- struct SListNode* next;//指向下一个结点的指针
- }SLNode;//重定义名字方便后期使用
- //链表的打印操作
- void SLPrint(SLNode* phead) {
- assert(phead);//不能传入空指针
- SLNode* pcur = phead;
- //pointer cursor:指针光标,不让头结点丢失(虽然不会改变头结点的指向)
- while (pcur) {//等同于pcur!=NULL
- printf("%d->", pcur->data);//打印此结点的数据
- pcur = pcur->next;//使pcur指向下一个结点
- }
- printf("NULL\n");
- }
后面会涉及到新结点的插入,申请新结点可以封装成一个函数,避免代码冗余
- //新结点的申请
- SLNode* SLBuyNode(SLDataType x) {
- SLNode* node = (SLNode*)malloc(sizeof(SLNode));
- if (!node) {//返回值为空,申请失败(一般是空间不足了),直接退出
- perror("malloc fail");
- exit(1);
- }
- node->data = x;//将数据赋给data
- node->next = NULL;//将下一个节点置为NULL;
- return node;
- }
- //尾部插入
- void SLPushBack(SLNode** pphead, SLDataType x) {
- //注意在这里我们传参的是二级指针,因为我们需要在函数内部改变头结点的指向
- assert(pphead);//不能传NULL
- //新结点的申请
- SLNode* node=SLBuyNode(x);
- if (*pphead == NULL) {
- *pphead = node;
- }
- else {
- SLNode* pcur = *pphead;
- while (pcur->next) {//找到next元素为NULL的结点,也就是为链表尾部
- pcur = pcur->next;
- }
- pcur->next = node;
- }
-
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
测试运行一下:
- //头部插入
- void SLPushFront(SLNode** pphead, SLDataType x) {
- assert(pphead);
- SLNode* node = SLBuyNode(x);
- //这里需要处理头结点为空的情况吗?不需要,因为没有涉及到解引用其元素
- node->next = *pphead;
- *pphead = node;
- }
测试运行一下:
- //尾部删除
- void SLPopBack(SLNode** pphead) {
- assert(*pphead && pphead);//
- if (!(*pphead)->next) {//处理只有一个元素的情况
- free(*pphead);
- *pphead = NULL;
- }
- else {
- SLNode* prev = NULL;
- SLNode* pcur = *pphead;
- while (pcur->next) {//找到尾结点前一个结点
- prev = pcur;
- pcur = pcur->next;
- }
- free(pcur);//将尾结点释放
- pcur = NULL;
- prev->next = NULL;//将尾结点的前一个结点的next元素置为NULL
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
测试运行一下:
- //头部删除
- void SLPopFront(SLNode** pphead) {
- assert(*pphead && pphead);
- SLNode* next = (*pphead)->next;//保存头结点的下一个结点地址
- free(*pphead);//释放头结点
- *pphead = next;//使头结点指向下一个结点
- }
测试运行一下:
在链表中想要对指定位置进行操作不能使用下标,所以我们必须找到指定位置的地址才能对其进行操作。
- //查找
- SLNode* SLFind(SLNode* phead, SLDataType x) {
- assert(phead);
- SLNode* pcur = phead;
- while (pcur) {
- if (pcur->data == x) {
- return pcur;
- }
- pcur = pcur->next;
- }
- return NULL;
- }
- //在指定位置之前插入数据
- void SLInsert(SLNode** pphead, SLNode* pos, SLDataType x) {
- assert(pphead && pos && *pphead);//pos和pphead不能为空,以及pphead指向空间不能为空
- if (*pphead == pos) {//处理头插的情况
- SLPushFront(pphead,x);
- }
- else {
- SLNode* node = SLBuyNode(x);
- SLNode* pcur = *pphead;
- while (pcur->next != pos) {//找到pos前一个结点
- pcur = pcur->next;
- }
- node->next = pcur->next;
- pcur->next = node;
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
测试运行一下:
- //在任意位置之后插入数据
- void SLInsertAfter(SLNode* pos, SLDataType x) {
- assert(pos);//pos不能为空
- SLNode* node = SLBuyNode(x);
- node->next = pos->next;
- pos->next = node;
- }
- //删除pos结点
- void SLErase(SLNode** pphead, SLNode* pos) {
- assert(*pphead && pphead && pos);
- if (*pphead == pos) {//处理头删的情况
- SLPopFront(pphead);
- }
- else {
- SLNode* pcur = *pphead;
- while (pcur->next!= pos) {
- pcur = pcur->next;
- }
- pcur->next = pos->next;
- free(pos);
- pos = NULL;
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
测试运行一下:
- //删除pos之后结点
- void SLEraseAfter(SLNode* pos) {
- assert(pos && pos->next);//pos后面的结点也不能为空
- SLNode* next = pos->next;
- pos->next = next->next;
- free(next);
- next = NULL;
- }
测试运行一下:
- //销毁链表
- void SLDestory(SLNode** pphead) {
- assert(pphead && *pphead);
- SLNode* pcur = *pphead;
- while (pcur) {
- SLNode* nextnode = pcur->next;//使用一个变量接受下一个结点地址
- free(pcur);
- pcur = nextnode;
- }
- *pphead = NULL;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。