《C++》解密--单链表

目录

一、概念与结构

二、实现单链表

三、链表的分类

四、单链表算法题


一、概念与结构

        1、节点

          结点的组成主要有:当前结点要保存的数据和保存下一个节点的地址(指针变量)

        图中指针变量plist保存的是第一个结点的地址,我们称plist此时“指向”第一个结点。

        链表中每个结点都是独立申请的(如果需要插入数据时才去申请一块结点的空间),我们需            要通过指针变量来保存下一个结点位置才能从当前结点找到下一个结点。

        2、链表的性质

【链表机构在逻辑上是连续的,在物理结构上不一定连续。

【结点一般是从堆上申请的。

【从堆上申请来的空间,是按照一定策略分配出来的,每次申请的空间可能连续,可能不连续。

  

二、实现单链表

 1、创立文件

 2、定义链表的结构

【SList.h】
//定义链表(结点)的位置
typedef int SLTDataType;
typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SLTNode;void SLTPrint(SLTNode* phead);

3、打印函数

【SList.h】
//打印函数
void SLTPrint(SLTNode* phead);
【SList.c】
//打印函数
void SLTPrint(SLTNode* phead)
{SLTNode* pcur = phead;while (pcur){printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL\n");
}
【test.c】
//创建一个链表,并且打印链表
void createSList()
{SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));node1->data = 1;SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));node1->data = 2;SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));node1->data = 3;SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));node1->data = 4;node1->next = node2;node2->next = node3;node3->next = node4;node4->next = NULL;//第一个结点的地址作为参数传递过去SLTNode* plist = node1;SLTPrint(node1);
}

4、判断插入时,空间是否足够

【SList.c】
//判断插入数据时,空间是否足够
SLTNode* SLTBuyNode(SLTDataType x)
{SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));if (node == NULL){perror("malloc fail!");exit(1);}node->data = x;node->next = NULL;return node;
}

5、尾插

【SList.h】
//尾插
void SLTPushBack(SLTNode** pphead,SLTDataType x);
【SList.c】
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);//形参:pphead --> 实参:&plist//*pphead 解引用 --> plist//申请新结点SLTNode* newnode = SLTBuyNode(x); //判断是否有足够空间if (*pphead == NULL){*pphead = newnode;}else{//尾节点 --> 新结点//找尾节点SLTNode* ptail = *pphead;while (ptail->next){ptail = ptail->next;}//ptail --> newnodeptail->next = newnode;}
}
【test.c】
void SListTest01()
{SLTNode* plist = NULL;SLTPushBack(&plist, 1);SLTPrint(plist);  //1->NULLSLTPushBack(&plist, 2);SLTPrint(plist);  //1->2->NULLSLTPushBack(&plist, 3);SLTPrint(plist);  //1->2->3->NULLSLTPushBack(&plist, 4);SLTPrint(plist);  //1->2->3->4->NULL
}
【运行结果】

6、头插

【SList.h】
//头插
void SLTPushFront(SLTNode** pphead,SLTDataType x);
【SList.c】
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = SLTBuyNode(x); //判断是否有足够空间//newnode *ppheadnewnode->next = *pphead;*pphead = newnode;
}
【test.c】
void SListTest01()
{SLTNode* plist = NULL;//头插SLTPushFront(&plist, 1);SLTPushFront(&plist, 2);SLTPushFront(&plist, 3);SLTPushFront(&plist, 4);SLTPrint(plist);   //4->3->2->1->NULL
}
【运行结果】

7、尾删

【SList.h】
//尾删
void SLTPopBack(SLTNode** pphead);
【SList.c】
//尾删
void SLTPopBack(SLTNode** pphead)
{//判断链表是不是空,若为空则不可以删assert(pphead && *pphead);//处理只有一个结点的情况:要删除的就是头结点(当next指针为空)if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{//找尾结点ptail 和尾结点的前一个结点prevSLTNode* ptail = *pphead;SLTNode* prev = NULL;//遍历数组,找尾结点while (ptail->next){prev = ptail;ptail = ptail->next;}prev->next = NULL;free(ptail);ptail = NULL;}
}
【test.c】
void SListTest01()
{SLTNode* plist = NULL;//头插SLTPushFront(&plist, 1);SLTPushFront(&plist, 2);SLTPushFront(&plist, 3);SLTPushFront(&plist, 4);//尾删SLTPopBack(&plist);SLTPrint(plist);  //4->3->2->NULLSLTPopBack(&plist);SLTPrint(plist);  //4->3->NULLSLTPopBack(&plist);SLTPrint(plist);  //4->NULLSLTPopBack(&plist);SLTPrint(plist);  //NULL
}
【运行结果】

8、头删

【SList.h】
//头删
void SLTPopFront(SLTNode** pphead);
【SList.c】
//头删
void SLTPopFront(SLTNode** pphead)
{assert(pphead && *pphead);SLTNode* next = (*pphead)->next;//删除*pphead --> nextfree(*pphead);*pphead = next;
}
【test.c】
void SListTest01()
{SLTNode* plist = NULL;//头插SLTPushFront(&plist, 1);SLTPushFront(&plist, 2);SLTPushFront(&plist, 3);SLTPushFront(&plist, 4);//SLTPrint(plist);   //4->3->2->1->NULL//头删SLTPopFront(&plist);SLTPrint(plist);  //3->2->1->NULLSLTPopFront(&plist);SLTPrint(plist);  //2->1->NULLSLTPopFront(&plist);SLTPrint(plist);  //1->NULLSLTPopFront(&plist);SLTPrint(plist);  //NULL
}
【运行结果】

9、查找

【SList.h】
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
【SList.c】
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{assert(phead);SLTNode* pcur = phead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}//跳出循环,说明没找到return NULL;
}
【test.c】
void SListTest01()
{SLTNode* plist = NULL;//头插SLTPushFront(&plist, 1);SLTPushFront(&plist, 2);SLTPushFront(&plist, 3);SLTPushFront(&plist, 4);//SLTPrint(plist);   //4->3->2->1->NULL//查找SLTNode* find = SLTFind(plist, 3);if (find == NULL){printf("没找到!\n");}else{printf("find it!\n");}
}
【运行结果】

10、在指定位置之前插入数据

【SList.h】
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
【SList.c】
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos);//如果pos恰好是头结点if (pos == *pphead){SLTPushFront(pphead, x); //相当于头插}else{SLTNode* newnode = SLTBuyNode(x);//找到pos的前一个结点:prevSLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}//跳出这个循环,说明prev的前一个结点就是pos//将newnode插入prev和pos之间newnode->next = pos;prev->next = newnode;}
}
【test.c】
void SListTest01()
{SLTNode* plist = NULL;//头插SLTPushFront(&plist, 1);SLTPushFront(&plist, 2);SLTPushFront(&plist, 3);SLTPushFront(&plist, 4);SLTPrint(plist);   //4->3->2->1->NULL//查找SLTNode* find = SLTFind(plist, 2);//在指定位置之前插入数据SLTInsert(&plist, find, 11); //在2之前插入11SLTPrint(plist);   //4->3->11->2->1->NULL
}
【运行结果】

11、在指定位置之后插入数据

【SList.h】
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
【SList.c】
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTBuyNode(x);//把newnode放到pos的下一个位置(pos->next)newnode->next = pos->next;pos->next = newnode;
}
【test.c】
void SListTest01()
{SLTNode* plist = NULL;//头插SLTPushFront(&plist, 1);SLTPushFront(&plist, 2);SLTPushFront(&plist, 3);SLTPushFront(&plist, 4);SLTPrint(plist);   //4->3->2->1->NULL//查找SLTNode* find = SLTFind(plist, 2);//在指定位置之后插入数据SLTInsertAfter(find, 9);  //在2之后插入9SLTPrint(plist);  //4->3->2->9->1->NULL
}
【运行结果】

12、删除pos结点

【SList.h】
//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos);
【SList.c】
//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pos);assert(pphead && *pphead);//头插(如果pos恰好为头结点*pphead,那就没有前一个结点prev了)if (pos == *pphead){SLTPopFront(pphead);}else{SLTNode* prev = *pphead;while (prev->next != pos){ //遍历数组,寻找posprev = prev->next;}//跳出循环,prev刚好是pos的前一个结点(要删除pos)prev->next = pos->next;free(pos);pos = NULL;}}
【test.c】
void SListTest01()
{SLTNode* plist = NULL;//头插SLTPushFront(&plist, 1);SLTPushFront(&plist, 2);SLTPushFront(&plist, 3);SLTPushFront(&plist, 4);SLTPrint(plist);   //4->3->2->1->NULL//查找SLTNode* find = SLTFind(plist, 2);//删除pos结点SLTErase(&plist, find);  //删除pos(2)这个结点SLTPrint(plist);  //4->3->1->NULL
}
【运行结果】

13、删除pos之后的结点

【SList.h】
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos);
【SList.c】
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos)
{assert(pos);assert(pos->next);//删除pos的下一个结点,就要让pos的下一个结点直接指向pos的next的next//但是后面要把pos->next释放删除掉,所以先把这个指针保存起来SLTNode* del = pos->next;pos->next = pos->next->next;free(del);del = NULL;
}
【test.c】
void SListTest01()
{SLTNode* plist = NULL;//头插SLTPushFront(&plist, 1);SLTPushFront(&plist, 2);SLTPushFront(&plist, 3);SLTPushFront(&plist, 4);SLTPrint(plist);   //4->3->2->1->NULL//查找SLTNode* find = SLTFind(plist, 2);//删除pos之后的结点SLTEraseAfter(find);SLTPrint(plist);  //4->3->2->NULL
} 
【运行结果】

14、销毁链表

【SList.h】
//销毁链表
void SListDestroy(SLTNode** pphead);
【SList.c】
//销毁链表
void SListDestroy(SLTNode** pphead)
{assert(pphead);assert(*pphead);SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}
【test.c】
void SListTest01()
{SLTNode* plist = NULL;//头插SLTPushFront(&plist, 1);SLTPushFront(&plist, 2);SLTPushFront(&plist, 3);SLTPushFront(&plist, 4);SLTPrint(plist);   //4->3->2->1->NULL//查找SLTNode* find = SLTFind(plist, 2);//销毁链表SListDestroy(&plist);SLTPrint(plist);  //NULL
} 
【运行结果】

三、链表的分类

四、单链表算法题

1、反转链表

【思路】

【代码】
typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head)
{//处理空链表if(head == NULL){return head;}//创建三个指针ListNode* n1,*n2,*n3;n1 = NULL , n2 = head , n3 = n2->next;   while(n2){n2->next = n1;n1 = n2;n2 = n3;if(n3){n3 = n3->next;}}//此时n1就是链表反转后新的头结点headreturn n1;
}

2、链表的中间结点

【思路】

【代码】
typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head) {ListNode* slow = head,*fast = head;//慢指针每次走一步//快指针每次走两步while(fast && fast->next)  //等价于fast!=NULL && fast->next!=NULL
//当fast或fast->fast任何一个为空指针(为假)时,就跳出循环{slow = slow->next;fast = fast->next->next;}//此时slow指向得节点刚好就是中间节点return slow;}

3、合并两个有序链表

【思路】

【代码】
typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) 
{//处理链表为空的情况if(list1 == NULL){return list2;}if(list2 == NULL){return list1;}//创建新链表ListNode*newHead = NULL,*newTail = NULL;//创建两个指针分别指向两个链表的头结点ListNode* l1 = list1;ListNode* l2 = list2;//比大小while(l1 && l2){if(l1->val < l2->val){//l1尾插到新链表中if(newHead == NULL){newHead = newTail = l1;}else{newTail->next = l1;newTail = newTail->next;}l1 = l1->next;}else{//l2尾插到新链表if(newHead == NULL){newHead = newTail = l2;}else{newTail->next = l2;newTail = newTail->next;}l2 = l2->next;}}//跳出while循环,只有两种情况:l1为空(l2肯定不为空) 或 l2为空(l1不为)if(l1)  //l1不为空{newTail->next = l1;}if(l2)  //l2不为空{newTail->next = l2;}return newHead;
}

4、链表分割

【思路】

【代码】
class Partition 
{
public:ListNode* partition(ListNode* pHead, int x) { //创建两个非空链表:小链表、大链表//小链表ListNode* lessHead,*lessTail;lessHead = lessTail = (ListNode*)malloc(sizeof(ListNode));//大链表 ListNode* greaterHead,*greaterTail;greaterHead = greaterTail = (ListNode*)malloc(sizeof(ListNode));//遍历原链表,与x比较大小// >=x的尾插进入大链表 , <x的尾插进入小链表ListNode* pcur = pHead;while(pcur)  //等价于pcur != NULL{if(pcur->val < x){ //尾插到小链表lessTail->next = pcur;lessTail = lessTail->next;}else { //尾插到大链表greaterTail->next = pcur;greaterTail = greaterTail->next;}pcur = pcur->next;}//将大链表的尾结点的next置为空greaterTail->next = NULL;//让大小链表首尾相连lessTail->next = greaterHead->next;ListNode* ret = lessHead->next;free(lessHead);free(greaterHead);lessHead = greaterHead = NULL;return ret;}
};

5、链表的回文结构

【代码】
class PalindromeList 
{
public:
ListNode* findMidNode(ListNode* phead)
{//使用快慢指针,寻找中间结点ListNode* slow = phead;ListNode* fast = phead;while(fast && fast->next){slow = slow->next;fast = fast->next->next;}return slow;
}ListNode* reverseList(ListNode* phead)
{//链表反转ListNode* n1,*n2,*n3;n1 = NULL;n2 = phead;n3 = n2->next;while(n2){n2->next = n1;n1 = n2;n2 = n3;if(n3){n3 = n3->next;}}return n1;
}bool chkPalindrome(ListNode* A) {//1、找中间结点ListNode* mid = findMidNode(A);//2、根据中间结点,将中间结点后的链表反转ListNode* right = reverseList(mid);//3、从原链表的头和反转链表比较结点值 (开始时,left指头,right指尾)ListNode* left = A;while(right){if(left->val != right->val){return false;}left = left->next;right = right->next;}return true;}
};

未完待续...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1551281.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

极限电流型氧传感器的工作原理以及有哪些应用场景?

极限电流型氧传感器的工作原理&#xff1a; 极限电流型氧传感器的工作原理基于稳定ZrO2固体电解质的氧泵作用。在已稳定化ZrO2两侧被覆铂电极&#xff0c;阴极侧用有气体扩散孔的罩接合&#xff0c;形成阴极空腔。在一定的温度下&#xff0c;当ZrO2电极两侧加一定电压时&#…

【渗透实战系列】|App渗透 ,由sql注入、绕过人脸识别、成功登录APP

涉及知识点 1、APP抓包和逆向破解加密算法&#xff1b; 2、解密参数&#xff0c;寻找注入点&#xff1b; 3、Union注入构造万能密码&#xff1b; 4、利用忘记密码功能&#xff0c;burpsuite爆破用户名&#xff1b; 5、解密短信验证数据包&#xff0c;绕过验证码&#xff0c;成功…

Docker笔记-Docker磁盘空间清理

无用的容器指的是已经停止运行且处于非活跃状态的容器。无用的镜像包括没有被任何容器使用的镜像&#xff0c;或者是被标记为"<none>"的镜像&#xff0c;通常是构建过程中产生的无标签镜像。 通过执行 docker container ls -a 和 docker image ls -a 命令&…

2024年软考——系统规划与管理师30天冲刺学习指南!!!

距离2024下半年软考系统规划与管理师考试已经只剩一个多月了&#xff0c;还没有开始备考的小伙伴赶紧行动起来。为了帮助大家更好的冲刺学习&#xff0c;特此提供一份考前30天学习指南。本指南包括考情分析、学习规划、冲刺攻略三个部分&#xff0c;可以参考此指南进行最后的复…

墙绘艺术在线交易平台:SpringBoot技术详解

4 系统设计 墙绘产品展示交易平台的设计方案比如功能框架的设计&#xff0c;比如数据库的设计的好坏也就决定了该系统在开发层面是否高效&#xff0c;以及在系统维护层面是否容易维护和升级&#xff0c;因为在系统实现阶段是需要考虑用户的所有需求&#xff0c;要是在设计阶段没…

【视频目标分割-2024CVPR】Putting the Object Back into Video Object Segmentation

Cutie 系列文章目录1 摘要2 引言2.1背景和难点2.2 解决方案2.3 成果 3 相关方法3.1 基于记忆的VOS3.2对象级推理3.3 自动视频分割 4 工作方法4.1 overview4.2 对象变换器4.2.1 overview4.2.2 Foreground-Background Masked Attention4.2.3 Positional Embeddings 4.3 Object Me…

RabbitMQ的高级特性-死信队列

死信(dead message) 简单理解就是因为种种原因, ⽆法被消费的信息, 就是死信. 有死信, ⾃然就有死信队列. 当消息在⼀个队列中变成死信之后&#xff0c;它能被重新被发送到另⼀个交换器 中&#xff0c;这个交换器就是DLX( Dead Letter Exchange ), 绑定DLX的队列, 就称为死信队…

EasyX与少儿编程:轻松上手的编程启蒙工具

EasyX&#xff1a;开启少儿编程的图形化启蒙之路 随着科技发展&#xff0c;编程逐渐成为孩子们教育中重要的一部分。如何让孩子在编程启蒙阶段更容易接受并激发他们的兴趣&#xff0c;成为许多家长和老师关心的问题。相比起传统的编程语言&#xff0c;图形化编程工具显得更直观…

【ehr】人事招聘薪资绩效考勤一体化管理系统(源码)

一、项目介绍 一款全源码可二开&#xff0c;可基于云部署、私有部署的企业级数字化人力资源管理系统&#xff0c;涵盖了招聘、人事、考勤、绩效、社保、酬薪六大模块&#xff0c;解决了从人事招聘到酬薪计算的全周期人力资源管理&#xff0c;符合当下大中小型企业组织架构管理运…

2k1000LA loongnix 安装java

问题&#xff1a; 客户 需要在 loongnix 上 使用 java 的程序。 情况说明&#xff1a; 使用 apt get 是无法 安装java 的。 按照的资料就行。 首先是 下载 loongarch64 的 java 的压缩包。这个我已经下载下来了。 社区下载地址&#xff1a; http://www.loongnix.cn/zh/api/…

书生大模型实战训练营 第三期 入门岛

1.Linux 任务一 完成SSH连接与端口映射并运行hello_world.py vscode自带的端口设置功能很方便 2.Python 任务一 实现wordcount函数 任务二 vscode 单步调试

【递归】9. leetcode 104 二叉树的最大深度

1 题目描述 题目链接&#xff1a;二叉树的最大深度 2 解答思路 递归分为三步&#xff0c;接下来就按照这三步来思考问题 第一步&#xff1a;挖掘出相同的子问题 &#xff08;关系到具体函数头的设计&#xff09; 第二步&#xff1a;只关心具体子问题做了什么 &#xff0…

物联网智能设备:未来生活的变革者

文章目录 引言什么是物联网智能设备&#xff1f;技术架构应用场景挑战与解决方案未来发展趋势结论 引言 随着科技的迅猛发展&#xff0c;物联网&#xff08;IoT&#xff09;正在改变我们生活的方方面面。从智能家居到工业自动化&#xff0c;物联网智能设备正在逐步融入我们的日…

四款视频剪辑工具使用感受与推荐:

大家好&#xff01;今天咱们来聊聊视频剪辑工具。随着短视频的火热&#xff0c;越来越多的小伙伴开始涉足视频剪辑领域&#xff0c;那到底哪款工具更适合你呢&#xff1f;接下来&#xff0c;就让我为大家分享一下我使用过的几款视频剪辑工具的体验和感受吧&#xff01; 一、福昕…

[linux 驱动]input输入子系统详解与实战

目录 1 描述 2 结构体 2.1 input_class 2.2 input_dev 2.4 input_event 2.4 input_dev_type 3 input接口 3.1 input_allocate_device 3.2 input_free_device 3.3 input_register_device 3.4 input_unregister_device 3.5 input_event 3.6 input_sync 3.7 input_se…

如何做好接口测试?||关于京东平台商品API接口测试

探讨主题&#xff1a;如何做好接口测试&#xff1f; 一、接口测试简介 1、什么是接口测试&#xff1f; 接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制…

Clocking System

文章目录 1. 介绍2. 时钟源2.1 scillator Circuit (OSC)2.1.1 外部时钟输入模式2.1.2 外部晶体/陶瓷谐振器模式2.1.3 振荡器的配置2.1.4 Oscillator Watchdog 2.2 Back-up Clock 3. 锁相环&#xff08;PLL&#xff09;3.1 系统锁相环3.1.1 Features3.1.2 框图 3.2.外设锁相环3.…

普中51单片机

参考&#xff1a;51单片机快速入门教程2022&#xff08;普中51开发板A2新版&#xff09;--绍兴文理学院元培学院《单片机原理与应用》课程建设_哔哩哔哩_bilibili 1.以管理员启动&#xff0c;破解

python-pptx 中 placeholder 和 shape 有什么区别?

在 python-pptx 库中&#xff0c;placeholder 和 shape 是两个核心概念。虽然它们看起来相似&#xff0c;但在功能和作用上存在显著的区别。为了更好地理解这两个概念&#xff0c;我们可以通过它们的定义、使用场景以及实际代码示例来剖析其差异。 Python-pptx 的官网链接&…

KA客户关系管理策略全解析

在当今商业竞争日益激烈的环境中&#xff0c;如何有效管理和维护关键客户关系成为企业制胜的关键。无论是初创企业还是跨国公司&#xff0c;都面临着同样的挑战&#xff0c;那就是如何通过精准的客户关系管理策略&#xff0c;不仅保留现有客户&#xff0c;还能不断拓展新的商业…