当前位置: 首页 > news >正文

顺序表与链表·续

引言

本文承接上文(顺序表与链表-CSDN博客),开始对链表的要点提炼。前文提到顺序表适合需要频繁随机访问且数据量固定的场景,而链表适合需要频繁插入和删除且数据量动态变化的场景。链表的引入弥补了顺序表在动态性和操作效率上的不足,是线性表的重要实现方式之一。

链表

概念

链表是一种 物理存储结构上非连续 、非顺序的存储结构,数据元素的 逻辑顺序 是通过链表
中的 指针链接 实现的 。
链表结构
容易发现链表结构在逻辑上连续,但物理上不一定连续,一般链表的节点从堆上申请的,每次申请的空间是否连续是不确定的。

分类

链表的分类可以根据以下维度进行:

  1. 单向或双向:决定节点的指针数量和遍历方向。

  2. 带头或不带头:决定是否有额外的头节点简化操作。

  3. 循环或不循环:决定链表是否形成环。

通过组合这些维度,可以设计出适合不同场景的链表结构。例如:

  • 带头单向循环链表:适合实现队列。

  • 带头双向循环链表:适合需要双向遍历且操作简化的场景。

  而我们常遇到的主要是不带头单向非循环链表和带头双向循环链表(以下图例分别对应这两种链表)

不带头单向非循环链表
带头双向循环链表

 

实现

无头单向非循环链表的简单实现

//"slist.h"
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int SLTDateType;typedef struct SListNode
{SLTDateType data;struct SListNode* next;
}SListNode;// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x) 
{SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL){perror("malloc fail");exit(-1);}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)
{SListNode* newnode = BuySListNode(x);if (*pplist == NULL) {*pplist = newnode;}else{SListNode* tail = *pplist;while (tail->next!=NULL){tail = tail->next;}tail->next = newnode;}}
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{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;}//多个节点SListNode* tail = *pplist;while (tail->next->next!=NULL){tail = tail->next;}free(tail->next);tail->next = NULL;}
void SListPopFront(SListNode** pplist) {// 防御性检查:拦截非法输入if (pplist == NULL) {fprintf(stderr, "Error: Invalid pointer in SListPopFront\n");return;}// 业务逻辑检查:空链表直接返回if (*pplist == NULL) {return;}SListNode* tmp = *pplist;*pplist = tmp->next;free(tmp);
}// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{SListNode* cur = plist;while (cur) {if (cur->data == x) {return cur;}cur = cur->next;}return NULL;
}
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?  // 因为没有前置指针
// 若要在 pos 之前插入,必须从头节点开始遍历链表找到 pos 的前驱节点,时间复杂度为 O(n)
void SListInsertAfter(SListNode* pos, SLTDateType x)
{if (pos == NULL) return;SListNode* newNode = BuySListNode(x);newNode->next = pos->next;pos->next = newNode;
}
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置? //删除 pos 节点需要知道其前驱节点,而单链表无法直接获取前驱节点
// 必须从头遍历链表,时间复杂度为O(n),删除 pos 之后的节点只需修改 pos->next,时间复杂度为O(1)。void SListEraseAfter(SListNode* pos)
{if (pos == NULL || pos->next == NULL) return;SListNode* tmp = pos->next;pos->next = tmp->next;free(tmp);
}// 在pos的前面插入
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDateType x)
{assert(pphead);assert(pos);assert(*pphead);if (*pphead == pos) SListPushFront(pphead,x);else{SListNode* prev = *pphead;while (prev->next!=pos){prev = prev->next;}SListNode* newnode=BuySListNode(x);prev->next = newnode;newnode->next = pos;}
}// 删除pos位置
void SLTErase(SListNode** pphead, SListNode* pos)
{assert(pphead);assert(*pphead);assert(pos);if (*pphead == pos){// 头删SListPopFront(pphead);}else{SListNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}void SLTDestroy(SListNode** pphead)
{assert(pphead);SListNode* cur = *pphead;while (cur) {SListNode* tmp = cur;cur = cur->next;free(tmp);}*pphead = NULL;
}

关键点说明

  1. 二级指针的使用

    • 修改头指针时(如插入/删除头节点),需通过二级指针 pplist 修改一级指针 *pplist

    • 示例:SListPushFront 和 SListPopFront 直接修改头指针。

  2. 边界条件处理

    • 空链表、单节点链表、尾节点/头节点操作需特殊处理。

    • 例如 SListPopBack 中需处理链表只有一个节点的情况。

  3. 时间复杂度

    • 头插/头删:O(1)/O(1)

    • 尾插/尾删:O(n)/O(n)

    • 插入/删除指定位置:平均 O(n)/O(n)(需遍历找到位置)

  4. 内存管理

    • 每次 malloc 后需检查内存分配是否成功。

    • 删除节点后需及时 free 防止内存泄漏。

  若需要频繁在任意位置前插入或删除,最好使用双向链表,它可以通过前驱指针直接操作,时间复杂度为 O(1)/O(1)。

相关文章:

顺序表与链表·续

引言 本文承接上文&#xff08;顺序表与链表-CSDN博客&#xff09;&#xff0c;开始对链表的要点提炼。前文提到顺序表适合需要频繁随机访问且数据量固定的场景&#xff0c;而链表适合需要频繁插入和删除且数据量动态变化的场景。链表的引入弥补了顺序表在动态性和操作效率上的…...

nvidia驱动升级-ubuntu 1804

升级 1.从官网下载*.run驱动文件 2.卸载原始驱动 sudo /usr/bin/nvidia-uninstall sudo apt-get --purge remove nvidia-\* # 可能不需要加-\ sudo apt-get purge nvidia-\* # 可能不需要加-\ sudo apt-get purge libnvidia-\* # 可能不需要…...

【Linux】——初识操作系统

文章目录 冯-诺依曼体系结构操作系统shell 冯-诺依曼体系结构 我们现在所使用的计算机就是冯-诺依曼体系结构。 存储器就是内存。 由下图可知&#xff0c;寄存器最快&#xff0c;为啥不用寄存器呢&#xff1f; 因为越快价格就最贵&#xff0c;冯诺依曼体系结构的诞生&#xf…...

本地化deepseek

小白都能拥有自己的人工智能 1、我本地环境 系统:win10 cpu:i7(i7-12700),差不多就行 硬盘:500G+2T,可以不用这么大 显卡:七彩虹2060 12G ,够用了 我的配置最高也只能配上8B了, R1模型版本CPUGPU内存存储8B Intel Core i7/AMD Ryzen 7 及以上 无强制要求,有 4…...

利用可变参数模板,可打印任意参数和参数值。(C++很好的调式函数)

很酷的应用&#xff1a; &#xff08;1&#xff09; 如何获取可变参数名 代码例子&#xff1a; #define _test(...) (test_t(#__VA_ARGS__, __VA_ARGS__))template<typename... Args> void test_t(const char* names, Args... args) {std::cout << names <<…...

Yashan DB 体系结构

一、体系结构概况 1.1 线程管理 YashanDB采用多线程架构&#xff0c;线程分为两类&#xff1a; • 工作线程&#xff08;Worker Threads&#xff09;&#xff1a;每个客户端连接到数据库实例时&#xff0c;会创建一个工作线程。工作线程负责处理客户端的SQL请求&#xff0c;执…...

测试工程师Deepseek实战之如何反向PUA它

问: 你是一名资深测试开发工程师 帮我设计一个提效工具&#xff0c;具有以下功能&#xff1a; 1.页面使用PYQT5设计&#xff0c;用两个输入控件&#xff0c;最好是日期类型的控件&#xff0c;第一个日期控件作为开始日期&#xff0c;第二个日期控件作为结束日期&#xff1b;前后…...

Windows系统中在VSCode上配置CUDA环境

前置步骤 安装符合GPU型号的CUDA Toolkit 配置好 nvcc 环境变量 安装 Visual Studio 参考https://blog.csdn.net/Cony_14/article/details/137510909 VSCode 安装插件 Nsight Visual Studio Code Editionvscode-cudacpp 安装 cmake 并配置好环境变量 注&#xff1a;Windows 端…...

React Native 0.76 升级后 APK 体积增大的原因及优化方案

在将 React Native 从 0.71 升级到 0.76 后,打包体积从 40 多 MB 增加到了 80 MB。经过一系列排查和优化,最终找到了解决方案,并将优化过程整理如下。 1. React Native 0.76 体积增大的可能原因 (1) 新架构默认启用 React Native 0.76 默认启用了 New Architecture(新架…...

pycharm找不到conda可执行文件

conda 24.9.2 在pycharm的右下角就可以切换python解释器了...

定时任务框架

常用定时任务框架 JDK 自带的 ScheduledExecutorService 适用于轻量级定时任务&#xff0c;基于线程池实现。API 简单&#xff0c;适用于小规模任务调度。 Quartz 强大的 Java 任务调度框架&#xff0c;支持 Cron 表达式、分布式集群、持久化等。适用于复杂调度场景&#xff0…...

ESP32S3读取数字麦克风INMP441的音频数据

ESP32S3 与 INMP441 麦克风模块的集成通常涉及使用 I2S 接口进行数字音频数据的传输。INMP441 是一款高性能的数字麦克风&#xff0c;它通过 I2S 接口输出音频数据。在 Arduino 环境中&#xff0c;ESP32S3 的开发通常使用 ESP-IDF&#xff08;Espressif IoT Development Framew…...

利用后缀表达式构造表达式二叉树的方法

后缀表达式&#xff08;逆波兰表达式&#xff09;是一种将运算符放在操作数之后的表达式表示法。利用后缀表达式构造表达式二叉树的方法主要依赖于栈结构。 转换步骤 初始化 创建一个空栈。 遍历后缀表达式 对后缀表达式的每个符号依次处理&#xff1a; 遇到操作数 如果当前符…...

使用express创建服务器保存数据到mysql

创建数据库和表结构 CREATE DATABASE collect;USE collect;CREATE TABLE info (id int(11) NOT NULL AUTO_INCREMENT,create_date bigint(20) DEFAULT NULL COMMENT 时间,type varchar(20) DEFAULT NULL COMMENT 数据分类,text_value text COMMENT 内容,PRIMARY KEY (id) ) EN…...

YOLOv12本地部署教程——42%速度提升,让高效目标检测触手可及

YOLOv12 是“你只看一次”&#xff08;You Only Look Once, YOLO&#xff09;系列的最新版本&#xff0c;于 2025 年 2 月发布。它引入了注意力机制&#xff0c;提升了检测精度&#xff0c;同时保持了高效的实时性能。在保持速度的同时&#xff0c;显著提升了检测精度。例如&am…...

SQLAlchemy系列教程:如何防止SQL注入

SQL注入是一种常见的安全漏洞&#xff0c;它允许攻击者通过应用程序的SQL查询操纵数据库。使用ORM工具&#xff08;如SQLAlchemy&#xff09;提供的内置功能可以帮助减轻这些风险。本教程将指导您完成保护SQLAlchemy查询的实践。 了解SQL注入 当攻击者能够通过用户输入插入或操…...

1. 树莓派上配置机器人环境(具身智能机器人套件)

1. 安装树莓派系统 镜像下载地址&#xff08;windows/Mac/Ubuntu)&#xff0c;安装Pi5. 2. 环境配置&#xff08;登录Pi系统&#xff09; 2.1 启用 SSH From the Preferences menu, launch Raspberry Pi Configuration. Navigate to the Interfaces tab. Select Enable…...

基于SpringBoot的智慧停车场小程序(源码+论文+部署教程)

运行环境 • 前端&#xff1a;小程序 Vue • 后端&#xff1a;Java • IDE工具&#xff1a;IDEA&#xff08;可自行选择&#xff09; HBuilderX 微信开发者工具 • 技术栈&#xff1a;小程序 SpringBoot Vue MySQL 主要功能 智慧停车场微信小程序主要包含小程序端和…...

【从零开始学习计算机科学】数字逻辑(九)有限状态机

【从零开始学习计算机科学】数字逻辑(九)有限状态机 有限状态机状态机的表示方法有限状态机的Verilog描述有限状态机 有限状态机(简称状态机)相当于一个控制器,它将一项功能的完成分解为若干步,每一步对应于二进制的一个状态,通过预先设计的顺序在各状态之间进行转换,状…...

HarmonyOS Next~鸿蒙系统ArkCompiler跨平台编译技术的革新实践

HarmonyOS Next~鸿蒙系统ArkCompiler跨平台编译技术的革新实践 引言 在万物互联时代&#xff0c;操作系统对编译技术的需求已从单纯的代码转换演变为跨设备协同、高效资源调度与极致性能优化的综合挑战。华为鸿蒙系统&#xff08;HarmonyOS&#xff09;自主研发的ArkCompiler…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

sqlserver 根据指定字符 解析拼接字符串

DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

高防服务器能够抵御哪些网络攻击呢?

高防服务器作为一种有着高度防御能力的服务器&#xff0c;可以帮助网站应对分布式拒绝服务攻击&#xff0c;有效识别和清理一些恶意的网络流量&#xff0c;为用户提供安全且稳定的网络环境&#xff0c;那么&#xff0c;高防服务器一般都可以抵御哪些网络攻击呢&#xff1f;下面…...