【数据结构】_以单链表为例分析各种方法实现的特殊情况考虑思路

目录

1. 尾插SLTPushBack

2. 头插SLTPushFront

3. 尾删SLTPopBack

4. 头删SLTPopFront

5. 指定位置前插入

6. 指定位置前删除


对于每一种方法的具体实现,都不能仅简单考虑链表具有多个结点的情况,对于空链表等特殊情况都需特殊情况特殊分析,才能保证不出现空指针解引用等情况。

现以某几个方法为例,分析特殊情况的考虑思路。

1. 尾插SLTPushBack

1、考虑具有若干结点的情况,创建结构体指针变量curNode,令其遍历现有链表直至指向一个后继指针域为NULL的结点为止。令该后继指针域为NULL即可:

void SLTPushBack(SLTNode** pphead, SLTDataType x) {SLTNode* curNode = *pphead;while (curNode->next) {curNode = curNode->next;}curNode->next = newNode;
}

2、由于需对pphead进行解引用操作,则需保证其不为空,增加断言:

assert(pphead);

3、由于为增加结点操作,原链表可为空,无需对*pphead进行断言。

4、由于为增加结点操作,考虑当前链表为空的情况,即*pphead=NULL,又将*pphead复制给了curNode,故curNode=NULL,若采取与普通链表即情况1的相同处理模式,则遍历链表找尾结点的while (curNode->next)操作会导致空指针的解引用操作,故需对原链表是否为空进行单独讨论

讨论逻辑为:直接将创建的新结构体变量赋值给链表的头指针*pphead即可:

void SLTPushBack(SLTNode** pphead, SLTDataType x) {assert(pphead);SLTNode* newNode = SLTCreatNode(x);// 空链表if (*pphead == NULL) {*pphead = newNode;}else {// 非空链表SLTNode* curNode = *pphead;while (curNode->next) {curNode = curNode->next;}curNode->next = newNode;}
}

2. 头插SLTPushFront

1、考虑具有若干结点的链表:

创建新结点,令新结点的后继指针域指向原头结点,再令新结点为链表的头结点:

void SLTPushFront(SLTNode** pphead, SLTDataType x) {SLTNode* newNode = SLTCreatNode(x);newNode->next = *pphead;// 令新结点为链表的头结点*pphead = newNode;
}

 2、由于需对pphead进行解引用操作,故需保证pphead不为空,增加断言:

assert(pphead);

3、由于为增加结点操作,故原链表可为空,即可无结点,即允许*pphead=NULL,无需断言;

4、由于为增加结点操作,考虑当前链表为空的情况,即*pphead=NULL,若采取与普通链表相同的处理模式,即创建新结点后令其指针域指向空,再令新结点为新的头结点,并无差错,无需单独讨论处理:

void SLTPushFront(SLTNode** pphead, SLTDataType x) {assert(pphead);SLTNode* newNode = SLTCreatNode(x);newNode->next = *pphead;// 令新结点为链表的头结点*pphead = newNode;
}

3. 尾删SLTPopBack

1、考虑具有若干结点的链表:

遍历找到尾结点及尾结点的前趋结点,释放尾结点并令尾结点的前趋结点的指针域为空;

void SLTPopBack(SLTNode** pphead) {SLTNode* tailPrevNode = *pphead;SLTNode* tailNode = *pphead;while (tailNode->next) {tailPrevNode = tailNode;tailNode = tailNode->next;}free(tailNode);tailNode = NULL;tailPrevNode->next = NULL;
}

2、由于需对pphead进行解引用,需保证pphead不为空:

3、由于要删除结点,故链表不可无结点,需保证*pphead不为空;

结合1、2点,增加断言:

assert(pphead && *pphead);

4、由于要删除结点,考虑删除后链表为空即链表仅有一个结点的情况

若采取同普通链表相同的处理逻辑,则遍历找尾结点的while (tailNode->next)操作会造成空指针解引用,故而该情况需单独讨论。

处理逻辑为:直接释放链表的唯一结点*pphead,并将其置空。无需遍历查找:

void SLTPopBack(SLTNode** pphead) {assert(pphead && *pphead);// 链表只有一个结点if ((*pphead)->next == NULL) {free(*pphead);*pphead = NULL;}// 链表有多个结点else {SLTNode* tailPrevNode = *pphead;SLTNode* tailNode = *pphead;while (tailNode->next) {tailPrevNode = tailNode;tailNode = tailNode->next;}free(tailNode);tailNode = NULL;tailPrevNode->next = NULL;}
}

4. 头删SLTPopFront

1、考虑具有若干结点的链表,创建结构体指针记录链表第二个结点的地址(链表头结点的后继指针域),释放头结点,令第二个结点为新的头结点:

void SLTPopFront(SLTNode** pphead, SLTDataType x) {SLTNode* secNode = (*pphead)->next;free(*pphead);*pphead = secNode;
}

2、由于需对pphead进行解引用,故需保证pphead不为空;

3、由于是删除结点操作,则需保证链表不能无结点,即链表不为空,即*pphead不为空;

结合2、3,增加断言:

	assert(pphead && *pphead);

4、由于是删除结点操作,考虑删除后链表为空(即链表只有一个结点)的情况:

若采取普通链表相同的处理逻辑,则链表头结点的后继指针域为NULL,再将NULL作为链表的新的头指针,并无差错,无需单独讨论,使用普通链表的处理逻辑即可。

5. 指定位置前插入

1、考虑链表具有若干结点的情况:

创建新结点,找到指定结点pos的前驱结点posPrevNode,令posPrevNode结点的后继指针域指向新结点,再令新结点的后继指针域指向pos结点:

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) {SLTNode* newNode = SLTCreatNode(x);SLTNode* posPrevNode = *pphead;while (posPrevNode->next != pos) {posPrevNode = posPrevNode->next;}posPrevNode->next = newNode;newNode->next = pos;
}

2、由于需对pphead解引用,故pphead不可为空。

3、由于为指定位置前增加结点,若链表为空或指定位置为空则无意义,故*pphead与pos均不为空。增加断言:

assert(pphead && *pphead && pos);

4、由于为指定位置前增加结点,最特殊的情况是指定位置为头结点,考虑pos=*pphead的情况,若采取与普通链表相同的处理模式,则无法找到某一结点posPrevNode的后继指针域指向*pphead,故而pos=*pphead的情况须单独讨论处理。

处理逻辑为:当pos=*pphead时,即向链表进行头插,调用已完成的头插方法即可:

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) {assert(pphead && *pphead && pos);SLTNode* newNode = SLTCreatNode(x);if (pos == *pphead) {// 调用头插方法SLTPushFront(pphead, x);}else {SLTNode* posPrevNode = *pphead;while (posPrevNode->next != pos) {posPrevNode = posPrevNode->next;}posPrevNode->next = newNode;newNode->next = pos;}
}

6. 指定位置前删除

1、考虑链表具有若干结点的情况:

遍历查找指定位置结点的前驱结点posPrevNode,令其后继指针域指向pos的后继结点,即

posPrevNode->next = pos->next即可:

2、由于需对pphead进行解引用操作,故pphead不可为空。

3、由于为指定位置前删除结点,若链表为空或指定位置为空则无意义,故*pphead与pos均不为空。增加断言:

assert(pphead && *pphead && pos);

4、由于为指定位置前删除结点,最特殊的情况是指定位置为头结点,考虑pos=*pphead的情况,若采取与普通链表相同的处理模式,则无法找到某一结点posPrevNode的后继指针域指向*pphead,故而pos=*pphead的情况须单独讨论处理。

处理逻辑为:当pos=*pphead时,即向链表进行头删,调用已完成的头删方法即可:

void SLTErase(SLTNode** pphead, SLTNode* pos) {assert(pphead && *pphead && pos);if (*pphead == pos) {// 调用头删方法SLTPopFront(pphead);}else {SLTNode* posPrevNode = *pphead;while (posPrevNode->next != pos) {posPrevNode = posPrevNode->next;}posPrevNode->next = pos->next;free(pos);pos = NULL;}
}

注:链表的方法还有很多,若仅考虑已有多个结点且增、删时都不影响头结点,或是空链表、仅有一个结点的链表等特殊情况,会使得方法并不完整。此文仅做示例。

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

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

相关文章

Hot100之普通数组

53最大子数组和 题目 思路解析 我们用一个dp数组来收集我们从左往右,加起来的最大的和 也就是我们的节点不是负数,那我们直接收集就好了 如果是负数,我们就用Max()比较是这个节点大还是当前节点大(这个情…

如何利用天赋实现最大化的价值输出-补

原文: https://blog.csdn.net/ZhangRelay/article/details/145408621 ​​​​​​如何利用天赋实现最大化的价值输出-CSDN博客 如何利用天赋实现最大化的价值输出-CSDN博客 引用视频差异 第一段视频目标明确,建议也非常明确。 录制视频的人是主动性…

新能源算力战争:为什么AI大模型需要绿色数据中心?

新能源算力战争:为什么AI大模型需要绿色数据中心? 近年来,人工智能(AI)大模型的爆发式增长正在重塑全球科技产业的格局。以GPT-4、Gemini、Llama等为代表的千亿参数级模型,不仅需要海量数据训练,更依赖庞大的算力支撑。然而,这种算力的背后隐藏着一个日益严峻的挑战——…

Spring Boot 中的事件发布与监听:深入理解 ApplicationEventPublisher(附Demo)

目录 前言1. 基本知识2. Demo3. 实战代码 前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 基本的Java知识推荐阅读: java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全&am…

unity学习24:场景scene相关生成,加载,卸载,加载进度,异步加载场景等

目录 1 场景数量 SceneManager.sceneCount 2 直接代码生成新场景 SceneManager.CreateScene 3 场景的加载 3.1 用代码加载场景,仍然build setting里先加入配置 3.2 卸载场景 SceneManager.UnloadSceneAsync(); 3.3 同步加载场景 SceneManager.LoadScene 3.3.…

【Android】布局文件layout.xml文件使用控件属性android:layout_weight使布局较为美观,以RadioButton为例

目录 说明举例 说明 简单来说,android:layout_weight为当前控件按比例分配剩余空间。且单个控件该属性的具体数值不重要,而是多个控件的属性值之比发挥作用,例如有2个控件,各自的android:layout_weight的值设为0.5和0.5&#xff0…

hot100_21. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1: 输入:l1 [1,2,4], l2 [1,3,4] 输出:[1,1,2,3,4,4] 示例 2: 输入:l1 [], l2 [] 输出:[…

4 [危机13小时追踪一场GitHub投毒事件]

事件概要 自北京时间 2024.12.4 晚间6点起, GitHub 上不断出现“幽灵仓库”,仓库中没有任何代码,只有诱导性的病毒文件。当天,他们成为了 GitHub 上 star 增速最快的仓库。超过 180 个虚假僵尸账户正在传播病毒,等待不…

Spring Boot项目中解决跨域问题(四种方式)

目录 一,跨域产生的原因二,什么情况下算跨域三,实际演示四,解决跨域的方法 1,CrossOrigin注解2,添加全局过滤器3,实现WebMvcConfigurer4,Nginx解决跨域5,注意 开发项目…

浅析DNS污染及防范

DNS污染(DNS Cache Poisoning)是一种网络攻击手段,通过篡改DNS服务器的缓存数据,将域名解析结果指向错误的IP地址,从而误导用户访问恶意网站或无法访问目标网站。这种攻击利用了DNS协议的特性,例如“只认第…

五. Redis 配置内容(详细配置说明)

五. Redis 配置内容(详细配置说明) 文章目录 五. Redis 配置内容(详细配置说明)1. Units 单位配置2. INCLUDES (包含)配置3. NETWORK (网络)配置3.1 bind(配置访问内容)3.2 protected-mode (保护模式)3.3 port(端口)配置3.4 timeout(客户端超时时间)配置3.5 tcp-keepalive()配置…

单细胞分析基础-第一节 数据质控、降维聚类

scRNA_pipeline\1.Seurat 生物技能树 可进官网查询 添加链接描述 分析流程 准备:R包安装 options("repos"="https://mirrors.ustc.edu.cn/CRAN/") if(!require("BiocManager")) install.packages("BiocManager",update = F,ask =…

Qt常用控件 输入类控件

文章目录 1.QLineEdit1.1 常用属性1.2 常用信号1.3 例子1,录入用户信息1.4 例子2,正则验证手机号1.5 例子3,验证输入的密码1.6 例子4,显示密码 2. QTextEdit2.1 常用属性2.2 常用信号2.3 例子1,获取输入框的内容2.4 例…

大模型培训讲师老师叶梓分享:DeepSeek多模态大模型janus初探

以下视频内容为叶梓分享DeepSeek多模态大模型janus的部署,并验证其实际效果,包括图生文和文生图两部分。 叶梓老师人工智能培训分享DeepSeek多模态大模型janus初探 DeepSeek 的多模态大模型 Janus 是一款强大的 AI 模型,专注于图像和文本的多…

Linux系统上安装与配置 MySQL( CentOS 7 )

目录 1. 下载并安装 MySQL 官方 Yum Repository 2. 启动 MySQL 并查看运行状态 3. 找到 root 用户的初始密码 4. 修改 root 用户密码 5. 设置允许远程登录 6. 在云服务器配置 MySQL 端口 7. 关闭防火墙 8. 解决密码错误的问题 前言 在 Linux 服务器上安装并配置 MySQL …

17.2 图形绘制7

版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。 17.2.9 字体 17.2.9.1 Font类 Font类定义特定的文本格式,包括字体、字号和样式特性。 Font常用属性: Na…

浅析DDOS攻击及防御策略

DDoS(分布式拒绝服务)攻击是一种通过大量计算机或网络僵尸主机对目标服务器发起大量无效或高流量请求,耗尽其资源,从而导致服务中断的网络攻击方式。这种攻击方式利用了分布式系统的特性,使攻击规模更大、影响范围更广…

90,【6】攻防世界 WEB Web_php_unserialize

进入靶场 进入靶场 <?php // 定义一个名为 Demo 的类 class Demo { // 定义一个私有属性 $file&#xff0c;默认值为 index.phpprivate $file index.php;// 构造函数&#xff0c;当创建类的实例时会自动调用// 接收一个参数 $file&#xff0c;用于初始化对象的 $file 属…

HarmonyOS NEXT:保存应用数据

用户首选项使用 用户首选项的特点 数据体积小、访问频率高、有加载速度要求的数据如用户偏好设置、用户字体大小、应用的配置参数。 用户搜选项&#xff08;Preferences&#xff09;提供了轻量级配置数据的持久化能力&#xff0c;支持订阅数据变化的通知能力。不支持分布式同…

C++编程语言:抽象机制:模板(Bjarne Stroustrup)

目录 23.1 引言和概观(Introduction and Overview) 23.2 一个简单的字符串模板(A Simple String Template) 23.2.1 模板的定义(Defining a Template) 23.2.2 模板实例化(Template Instantiation) 23.3 类型检查(Type Checking) 23.3.1 类型等价(Type Equivalence) …