数据结构与算法系列之单链表

💗 💗 博客:小怡同学
💗 💗 个人简介:编程小萌新
💗 💗 如果博客对大家有用的话,请点赞关注再收藏 🌞
这里写目录标题
- test.h
- SList.h
- 注意事项
- 一级指针与二级指针的使用
- assert的使用
- 空链表与顺序表的区别及优缺点
- 详解各接口函数的功能
test.h
#include "SList.h"
void Test()
{SLTNode* plist = NULL;SLPrint(plist);SLPushBack(&plist, 7);SLPushBack(&plist, 8);SLPushBack(&plist, 9);SLPushBack(&plist, 10);SLPrint(plist);//SLTNode* ret = SListFind(plist, 8);//SLPrint(plist);//SLisTEraseAfter(ret);//SLPrint(plist);//SListInsert(&plist, ret, 3);//SListInsertAfter(ret, 4);//SlistErase(&plist , ret);//SLisTEraseAfter(ret);//SLPrint(plist);
}int main()
{Test();
}
SList.h
```c
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int SLTDataType;typedef struct SListNode
{SLTDataType data;struct SList* next;
}SLTNode;void SLPrint(SLTNode* phead);//打印
void SLPushBack(SLTNode** pphead, SLTDataType x);//尾插
void SLTPushFront(SLTNode** pphead, SLTDataType x);//头插
void SLTPopBack(SLTNode** pphead);//尾删
void SLTPopFront(SLTNode** pphead);//头删
SLTNode* BuySLTode(SLTDataType x);//开辟一个节点
SLTNode* SListFind(SLTNode* phead, SLTDataType x);//找到x这个节点
void SListInsert(SLTNode** phead, SLTNode* pos, SLTDataType x);//在pos之前增加data为x这个节点
void SListInsertAfter(SLTNode* pos, SLTDataType x);//在pos之后增加data为x这个节点
void SlistErase(SLTNode** pphead, SLTNode* pos);//删除在pos之前,删除这个节点
void SLisTEraseAfter(SLTNode* pos);//删除pos之后的节点
# SList.c```c
#include "SList.h"
SLTNode* BuySLTode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc fail");return NULL;}newnode->data = x;newnode->next = NULL;return newnode;
}void SLPrint(SLTNode* phead)
{SLTNode* cur = phead;while (cur != NULL){printf("%d->",cur->data);cur = cur->next;}printf("NULL\n");
}void SLPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = BuySLTode(x);if (*pphead == NULL){*pphead = newnode;}else{SLTNode* tail = *pphead;while (tail->next != NULL){tail = tail->next;}tail ->next= newnode;}
}void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = BuySLTode(x);if (*pphead == NULL){*pphead = newnode;}else{newnode->next = *pphead;*pphead = newnode;}
}void SLTPopBack(SLTNode** pphead)
{assert(pphead);assert(*pphead);if ( (*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{SLTNode* tail = *pphead;SLTNode* prev = NULL;while (tail->next != NULL){prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;}
}void SLTPopFront(SLTNode** pphead)
{assert(pphead);assert(*pphead);SLTNode* cur = *pphead;*pphead = (*pphead)->next;free(cur);cur = NULL;}SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{SLTNode* cur = phead;while (cur){if (cur->data == x)return cur;cur = cur->next;}return NULL;
}void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);assert(pos);if (*pphead == pos){SLTPushFront(pphead, x);}else{SLTNode* cur = pphead;while (cur->next != pos){cur = cur->next;}SLTNode* newnode = BuySLTode(x);cur->next = newnode;newnode->next = pos;}}void SListInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = BuySLTode(x);newnode->next = pos->next;pos->next = newnode;
}void SlistErase(SLTNode**pphead, SLTNode* pos)
{assert(pphead);assert(pos);//assert(*pphead);if (*pphead == pos){SLTPopFront(pphead);}else{SLTNode* cur = *pphead;while (cur->next != pos){cur = cur->next;}cur->next= pos->next;free(pos);}}void SLisTEraseAfter(SLTNode* pos)
{assert(pos);assert(pos->next);SLTNode* cur = pos->next;pos->next = pos->next->next;free(cur);cur = NULL;
}
注意事项
一级指针与二级指针的使用
可以看得出来,接口函数传参时有的需要而二级指针,有的需要一级指针
总结为一句话,改变结构体地址需要二级指针,改变结构体(SLTNode)成员需要一级指针。
解析如下
1.根据c语言,实参传址调用与传值调用知识我们可以知道,改变元素大小需要传入地址。而改变地址,则需要传入地址的地址。因为形参只是实参的拷贝 函数销毁之后形参将不存在

2.改变结构体的地址,具体是如何改变呢
通过头指针的地址(pphead这个是二级指针),再解引用得到头指针(headnoe),就可以使用且改变了。

3.通过结构体(headnode),headnode->next可以得到下一个结构体node1
assert的使用
从SList.c可以看出 有时assert(pphead(二级指针)),有时assert(*pphead(一级指针)),
1.当有二级指针传入时,必须使用assert(pphead)断言因为 pphead是头指针的地址 ,虽然头指针 为NULL但是,头指针的地址是真实存在的(也就是地址中的地址)。
2.assert(*pphead)是当链表中已经没有节点存在了,不能进行删除结点的行为。

空链表与顺序表的区别及优缺点
区别
1.顺序表为空时 因为结构体不为NULL,只是 size ==0 链表 phead 为空时,没有节点则头指针为NULL。
2,为什么链表不能和顺序表一样直接size++就可以存入数据呢
因为链表每个节点的地址位置都是malloc函数开辟,每个起点位置可能都不一样。而顺序表相当是开辟一段连续内存空间。
优缺点
一.顺序表存储
优点:顺序表存储是将数据元素放到一块连续的内存存储空间,通过下标 访问高效,直接存储与删除
缺点:1.插入和删除比较慢,需要移动大量元素,
2.开辟空间是不可减少的,不可以动态增加长度,容易造成内存浪费
二 .链表存储
原理:没有空间限制,不会溢出,节省空间内存
优点:插入和删除速度快,只需要改变指针指向即可
缺点:不可通过下标访问,需要遍历节点
详解各接口函数的功能
void SLPrint(SLTNode* phead)
void SLPrint(SLTNode* phead)
{
//不改变结构体地址,只是循环打印数据所以传一级指针SLTNode* cur = phead;while (cur != NULL){printf("%d->",cur->data);cur = cur->next;}printf("NULL\n");
}
void SLPushBack(SLTNode** pphead, SLTDataType x)
assert(pphead);//因为二级指针,肯定存在,所以需要断言SLTNode* newnode = BuySLTode(x);//判断第一个节点是否为空,改变头节点if (*pphead == NULL){*pphead = newnode;}else{SLTNode* tail = *pphead;while (tail->next != NULL)//寻找最后一个元素{tail = tail->next;}tail ->next= newnode;//在最后的元素添加节点}
void SLTPushFront(SLTNode** pphead, SLTDataType x)
assert(pphead);//因为头插改变了头节点,所以传二级指针SLTNode* newnode = BuySLTode(x);//当没有节点时,增加节点if (*pphead == NULL){*pphead = newnode;}else{newnode->next = *pphead;//增加首节点*pphead = newnode;}
void SLTPopBack(SLTNode** pphead)
assert(pphead);//二级指针必须断言assert(*pphead);//因为当头节点为空时,不能删除//当只有一个节点时,释放头节点的空间if ( (*pphead)->next == NULL){free(*pphead);*pphead = NULL;}//当有两个或两个以上的节点时else{SLTNode* tail = *pphead;SLTNode* prev = NULL;while (tail->next != NULL)//找到最后一个元素,以及倒数第二个,所以定义了两个指针,因为只找到最后一个并释放后需要把倒数第二个节点(的next)设为NULL{prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;}
void SLTPopFront(SLTNode** pphead)
assert(pphead);//二级指针必定不为空assert(*pphead);//因为链表必须有节点才可以删除,所以需要断言SLTNode* cur = *pphead;*pphead = (*pphead)->next;free(cur);cur = NULL;
SLTNode* BuySLTode(SLTDataType x)
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//开辟一个新节点if (newnode == NULL){perror("malloc fail");return NULL;}//初始化newnode->data = x;newnode->next = NULL;return newnode;
SLTNode* SListFind(SLTNode* phead, SLTDataType x)
SLTNode* cur = phead;while (cur){if (cur->data == x)return cur;//找到返回此节点坐标cur = cur->next;}return NULL;//找不到返回NULL
void SListInsert(SLTNode** phead, SLTNode* pos, SLTDataType x)
assert(pphead);//二级指针必不为空assert(pos);//判断pos是否为有效地址//判断头节点是否可以插入if (*pphead == pos){SLTPushFront(pphead, x);}else{SLTNode* cur = pphead;while (cur->next != pos)//找到pos之前的位置{cur = cur->next;}SLTNode* newnode = BuySLTode(x);cur->next = newnode;//链接新的节点newnode->next = pos;}
void SListInsertAfter(SLTNode* pos, SLTDataType x)
assert(pos);//判断pos位置SLTNode* newnode = BuySLTode(x);//链接新的节点newnode->next = pos->next;pos->next = newnode;
void SlistErase(SLTNode** pphead, SLTNode* pos)
assert(pphead);//二级指针必不为空assert(pos);//pos位置是否合理assert(*pphead);/删除一个节点,所以链表必不为空//改变了头指针,地址改变需要二级指针if (*pphead == pos){SLTPopFront(pphead);}else{SLTNode* cur = *pphead;while (cur->next != pos){cur = cur->next;}cur->next= pos->next;free(pos);}
void SLisTEraseAfter(SLTNode* pos)
assert(pos);//判断pos位置是否合理assert(pos->next);//因为要删除pos的下一个位置的节点,所以只有一个节点不能删除SLTNode* cur = pos->next;pos->next = pos->next->next;free(cur);cur = NULL;

相关文章:
数据结构与算法系列之单链表
💗 💗 博客:小怡同学 💗 💗 个人简介:编程小萌新 💗 💗 如果博客对大家有用的话,请点赞关注再收藏 🌞 这里写目录标题test.hSList.h注意事项一级指针与二级指针的使用assert的使用空…...
MySQL基础
本单元目标 一、为什么要学习数据库 二、数据库的相关概念 DBMS、DB、SQL 三、数据库存储数据的特点 四、初始MySQL MySQL产品的介绍 MySQL产品的安装 ★ MySQL服务的启动和停止 ★ MySQL服务的登录和退出 ★ MySQL的常见命令和语法规范 五、…...
面试热点题:环形链表及环形链表寻找环入口结点问题
环形链表 问题: 给你一个链表的头节点 head ,判断链表中是否有环。 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接…...
【算法】DFS与BFS
作者:指针不指南吗 专栏:算法篇 🐾题目的模拟很重要!!🐾 文章目录1.区别2.DFS2.1 排列数字2.2 n-皇后问题3.BFS3.1走迷宫1.区别 搜索类型数据结构空间用途过程DFSstackO( n )不能用于最短路搜索到最深处&a…...
湖州银行冲刺A股上市:计划募资约24亿元,资产质量水平较高
3月4日,湖州银行股份有限公司(下称“湖州银行”)递交招股书,准备在上海证券交易所主板上市。本次冲刺上市,湖州银行计划募资23.98亿元,将在扣除发行费用后全部用于补充该行资本金。 湖州银行在招股书中表示…...
高性能网络I/O框架-netmap源码分析
前几天听一个朋友提到这个netmap,看了它的介绍和设计,确实是个好东西。其设计思想与业界不谋而合——因为为了提高性能,几个性能瓶颈放在那里,解决方法自然也是类似的。 netmap的出现,它既实现了一个高性能的网络I/O框…...
SpringBoot监听机制-以及使用
11-SpringBoot事件监听 Java中的事件监听机制定义了以下几个角色: ①事件:Event,继承 java.util.EventObject 类的对象 ②事件源:Source ,任意对象Object ③监听器:Listener,实现 java.util…...
若依学习——定时任务代码逻辑 详细梳理(springboot整合Quartz)
springboot整合Quartz关于若依定时任务的使用可以去看视频默认定时任务的使用关于springboot整合quartz的整合参考(150条消息) 定时任务框架Quartz-(一)Quartz入门与Demo搭建_quarzt_是Guava不是瓜娃的博客-CSDN博客(150条消息) SpringBoot整合Quartz_springboot quartz_桐花思…...
C++---最长上升子序列模型---拦截导弹(每日一道算法2023.3.4)
注意事项: 本题为"线性dp—最长上升子序列的长度"的扩展题,这里只讲贪心思路,dp去这个看。 题目: 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。 但是这种导弹拦截系统有一个缺陷:虽然它…...
【机器学习面试】百面机器学习笔记和问题总结+扩展面试题
第1章 特征工程 1、为什么需要对数值类型的特征做归一化? (1)消除量纲,将所有特征统一到一个大致相同的区间范围,使不同指标之间具由可比性; (2)可以加快梯度下降收敛的速度&#…...
【2021.12.28】ctf逆向中的迷宫问题(含exe及wp)
【2021.12.28】ctf逆向中的迷宫问题(含exe及wp) 文章目录【2021.12.28】ctf逆向中的迷宫问题(含exe及wp)1、迷宫简介(1)简单例子(2)一般的迷宫代码2、二维迷宫(1…...
WSL2使用Nvidia-Docker实现深度学习环境自由部署
1. Win11 显卡驱动的安装 注意:WSL2中是不需要且不能安装任何显卡驱动的,它的显卡驱动完全依赖于 Win11 中的显卡驱动,因此我们只需要安装你显卡对应的 Win11 版本显卡驱动版本(必须是 Win11 版本的驱动),…...
SpringBoot入门 - 配置热部署devtools工具
在SpringBoot开发调试中,如果我每行代码的修改都需要重启启动再调试,可能比较费时间;SpringBoot团队针对此问题提供了spring-boot-devtools(简称devtools)插件,它试图提升开发调试的效率。准备知识点什么是…...
CANFDNET-200U-UDP配置与数据收发控制
一、启动ZCANPRP,打开设备管理页面,选择类型CANFDNET-200U-UDP,如图1 图1 二、打开设备,启动,在相应页面如图2,配置协议,CANFD 加速,本地端口,IP地址,工作端口。 图2 三、发送相应数…...
嵌入式中backtrace的使用
大家好,我是bug菌~ backtrace主要用于调试程序时,能够打印出程序在运行过程中的函数调用栈,以帮助开发者快速定位程序出现异常或崩溃的原因。 通过backtrace的输出,开发者可以了解程序在哪个函数出现问题,…...
CV学习笔记-Faster-RCNN
Faster R-CNN 文章目录Faster R-CNN1. 目标检测算法1.1 计算机视觉有五大应用1.2 目标检测任务1.3 目标检测算法概述2. 边框回归(Bounding-Box regression)2.1 IoU2.2 统计学中的指标2.3 边框回归3. Faster-RCNN网络3.1 Conv layers3.2 Region Proposal …...
大型三甲医院云HIS系统源码 强大的电子病历+完整文档
医院HIS系统源码云HIS系统:SaaS运维平台多医院入驻强大的电子病历完整文档 有源码,有演示 一、系统概述 采用主流成熟技术,软件结构简洁、代码规范易阅读,SaaS应用,全浏览器访问前后端分离,多服务协同&am…...
如何使用Spring Cloud搭建高可用的Elasticsearch集群?详解Elasticsearch的安装与配置及Spring Boot集成的实现
Spring Cloud 是一个基于 Spring Boot 的微服务框架,它提供了一系列组件和工具,方便开发人员快速搭建和管理分布式系统。Elasticsearch 是一个开源的全文搜索引擎,也是一个分布式、高可用的 NoSQL 数据库。本篇博客将详细讲解如何使用 Spring…...
phpinfo包含临时文件Getshell全过程及源码
目录 前言 原理 漏洞复现 靶场环境 源码 复现过程 前言 PHP LFI本地文件包含漏洞主要是包含本地服务器上存储的一些文件,例如session文件、日志文件、临时文件等。但是,只有我们能够控制包含的文件存储我们的恶意代码才能拿到服务器权限。假如在服…...
ubuntu22.04 Desktop 服务器安装
操作系统 使用的是Uubntu22.04 Desktop的版本,系统安装后,默认开启了53端口和631端口 关闭udp 5353、53791端口(avahi-daemon服务) sudo systemctl stop avahi-daemon.socket avahi-daemon.service sudo systemctl disable ava…...
【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error
在前端开发中,JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作(如 Promise、async/await 等),开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝(r…...
