《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.mzph.cn/news/880667.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第十七章:c语言内存函数

1. memcpy使⽤和模拟实现 2. memmove使⽤ 3. memset函数的使⽤ 4. memcmp函数的使⽤ 天行健 君子以自强不息一、memcpy的使用和模拟实现 作用&#xff1a; 1. 函数memcpy从source的位置向后复制num个字节的数据到destination指向的内存位置。 2. 这个函数在遇到‘\0’的时…

H.264编解码工具 - Intel Quick Sync Video

一、简介 Intel Quick Sync Video是英特尔的一个硬件加速技术,用于提高视频编码和解码的性能。它是英特尔处理器中集成的多媒体引擎的一部分。通过利用硬件加速,Quick Sync Video可以大幅提高视频处理性能,同时减少对CPU的负载。 Quick Sync Video支持多种编解码器,包括H…

MySQL - 进阶篇

一、存储引擎 1. MySQL体系结构 2. 存储引擎简介 3. 存储引擎特点 4. 存储引擎选择 二、索引 1. 索引概述 2. 索引结构 3. 索引分类 4. 索引语法 5. SQL性能分析 6. 索引使用 7. 索引设计原则 三、SQL优化 1. 插入数据 2. 主键优化 3. order by优化 4. group by优化 5. limi…

图像处理案例04

图像处理 问题&#xff1a;把不规则的图片按照参考图摆放 步骤&#xff1a; 1. 用ORB找关键点 2. 关键点匹配 3. 根据上一步匹配的关键点得出单应性矩阵 4. 根据单应性矩阵对不规则进行透视变换 import cv2 import numpy as np import matplotlib.pyplot as pltimgl cv2.imrea…

Hive数仓操作(五)

一、Hive 信息查看 Hive的元数据管理&#xff1a; Hive 将表的元数据&#xff08;如表名、列名、类型等&#xff09;存储在关系型数据库中&#xff0c;通常是 MySQL。元数据的主要表包括&#xff1a; TBLS&#xff1a;存储表的信息&#xff08;表名、类型、ID 等&#xff09;。…

【小程序】微信小程序课程 -4 项目实战

目录 1、 效果图 2、创建项目 2.1 创建小程序端 2.1.1 先创建纯净项目 2.1.2 删除components 2.1.4 删除app.json红色部分 2.1.5 删除index.json红色部分 2.1.6 删除index.wxss全部内容 2.1.7 删除index.wxml全部内容 2.1.8 app.json创建4个页面 2.1.9 app.json添加…

Python 枚举类型详解

Python 枚举类型详解 什么是枚举类型&#xff1f;枚举类型用法定义枚举类型访问枚举成员枚举的比较枚举迭代 枚举的特殊用法自动赋值枚举中的别名 枚举应用场景状态机配置选项标志操作 什么是枚举类型&#xff1f; 枚举&#xff08;Enumeration&#xff09;是一种数据类型&…

15年408计算机网络

第一题&#xff1a; 解析&#xff1a; 接收方使用POP3向邮件服务器读取邮件&#xff0c;使用的TCP连接&#xff0c;TCP向上层提供的是面向连接的&#xff0c;可靠的数据传输服务。 第二题&#xff1a; 解析&#xff1a;物理层-不归零编码和曼彻斯特编码 编码1&#xff1a;电平在…

GAMES101(作业8)

作业8 题目&#xff1a; 模拟绳子动画&#xff0c;包括基于物理的&#xff0c;和非物理的&#xff0c;应该修改的函数是:rope.cpp 中的void Rope::simulateEuler(... Rope::rope(...)&#xff0c;&#xff0c;void Rope::simulateVerlet(...) 代码框架&#xff1a; main:负…

centos磁盘逻辑卷LVM创建

centos磁盘逻辑卷LVM创建 一、磁盘逻辑卷LVM说明二、centos磁盘使用情况三、LVM安装指南1.LVM工具安装1. yum list lvm2. yum search lvm3. yum search pvcreate4. yum list lvm25. yum install lvm2 2.创建物理卷2.1磁盘情况查看2.2创建物理卷&#xff08;PV&#xff09; 3.创…

使用Postman工具接口测试

文章目录 一、接口1.1 接口的概念1.2 接口的类型 二、接口测试2.1 概念2.2 原理2.3 特点 三、HTTP协议3.1 http协议简介3.2 URL格式3.3 HTTP请求3.3.1 请求行3.3.2 请求头3.3.3 请求体 3.4 HTTP响应3.4.1 状态行3.4.2 响应头3.4.3 响应体 3.4 传统风格接口3.5 RESTful风格接口 …

Race Karts Pack 全管线 卡丁车赛车模型素材

是8辆高细节、可定制的赛车,内部有纹理。经过优化,可在手机游戏中使用。Unity车辆系统已实施-准备驾驶。 此套装包含8种不同的车辆,每种车辆有8-10种颜色变化,总共有75种车辆变化! 技术细节: -每辆卡丁车模型使用4种材料(车身、玻璃、车轮和BrakeFlare) 纹理大小: -车…

基于单片机的可调式中文电子日历系统

** 文章目录 前言概要功能设计软件设计效果图 程序文章目录 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们…

RSRP SNR SINR

RSRP&#xff08;Reference Signal Received Power&#xff0c;参考信号接收功率&#xff09;是衡量LTE网络中无线信号强度的关键参数之一&#xff0c;它表示在某个符号内承载参考信号的所有资源元素&#xff08;RE&#xff09;上接收到的信号功率的平均值。RSRP的数值通常在-4…

如何在O2OA中使用ElementUI组件进行审批流程工作表单设计

本文主要介绍如何在O2OA中进行审批流程表单或者工作流表单设计&#xff0c;O2OA主要采用拖拽可视化开发的方式完成流程表单的设计和配置&#xff0c;不需要过多的代码编写&#xff0c;业务人员可以直接进行修改操作。 在流程表单设计界面&#xff0c;可以在左边的工具栏找到Ele…

CSS预处理器LESS

LESS是一种CSS预处理器&#xff0c;它扩展了CSS的功能&#xff0c;通过引入变量、嵌套规则、Mixins&#xff08;混合&#xff09;、函数等特性&#xff0c;使得CSS的编写更加灵活和高效。以下是LESS的一些常用方式&#xff1a; 1. 变量 LESS允许定义变量&#xff0c;并在整个…

文心一言 VS 讯飞星火 VS chatgpt (357)-- 算法导论24.2 3题

三、上面描述的 PERT 图的公式有一点不太自然。在一个更自然的结构下&#xff0c;图中的结点代表要执行的工作&#xff0c;边代表工作之间的次序限制&#xff0c;即边 (u&#xff0c;v) 表示工作 u 必须在工作 v 之前执行。在这种结构的图中&#xff0c;我们将权重赋给结点&…

InnoDB 和 MyIsam 引擎的区别?数据库存储引擎的选择与解析:InnoDB 与 MyISAM 的全面对比

在当前的数字化时代&#xff0c;数据库不仅是保存信息的地方&#xff0c;更是业务运营的核心。MySQL 作为全球最流行的开源数据库之一&#xff0c;其灵活性和强大功能使得开发者和企业在选择存储引擎时需慎重考虑。本文将详细探讨 MySQL 中的两个主要存储引擎——InnoDB 和 MyI…

Spring面向对象的设计模式

在Spring框架中&#xff0c;面向对象的设计模式被广泛使用&#xff0c;这些设计模式能够提高代码的可维护性、可扩展性和复用性。以下是一些在Spring中常用的设计模式&#xff1a; 1. 单例模式&#xff08;Singleton Pattern&#xff09; 定义&#xff1a;保证一个类仅有一个…

httpsok-v1.17.0-SSL通配符证书自动续签

&#x1f525;httpsok-v1.17.0-SSL通配符证书自动续签 介绍 httpsok 是一个便捷的 HTTPS 证书自动续签工具&#xff0c;基于全新的设计理念&#xff0c;专为 Nginx 、OpenResty 服务器设计。已服务众多中小企业&#xff0c;稳定、安全、可靠。 一行命令&#xff0c;一分钟轻…