02-2.3.2_1 单链表的插入和删除

喜欢《数据结构》部分笔记的小伙伴可以订阅专栏,今后还会不断更新。
此外,《程序员必备技能》专栏和《程序员必备工具》专栏(该专栏暂未开设)日后会逐步更新,

插入

按位序插入

(1)带头结点

ListInsert(&L, i, e)插入操作——在表L中的i个位置上插入指定元素e
找到第i-1个结点,将新结点插入其后
具体代码实现:

//在第i个位置插入元素e(带头结点)
bool ListInsert(LinkList &L, int i, ElemType e){if(i<1)return false;LNode *p;//指针p指向当前扫描到的结点int j = 0;//当前p指向的是第几个结点p = L;//L指向头结点,头结点是第0个结点(不存数据)while(p != NULL && j < i-1){//循环找到i-1个结点p = p->next;j++;}if(p == NULL)//i值不合法return false;LNode *s = (LNode *) malloc(sizeof(LNode));s->data = e;s->next = p->next;p->next = s;//将结点s连到p之后return true;//插入成功
}

代码分析:

  • 如果i=1(插在表头)
  • 如果i=3,那么i-1=2,那么j=0p会指向下个结点,所以j=1,因为j=1 < 2,所以j会再+1变成2
  • 如果i=5(插在表尾),这种情况是最坏的,最坏时间复杂度= O ( n ) O(n) O(n)
  • 如果i=6i>length),执行之后发现p == NULL,返回false
    需要注意的是:
    s->next = p->next;p->next = s;两句顺序不能颠倒

(2)不带头结点

ListInsert(&L, i, e)插入操作——在表L中的i个位置上插入指定元素e
找到第i-1个结点,将新结点插入其后
但是因为不存在所谓的“第0个”结点,因此当i=1时需要进行特殊处理
具体代码实现:

bool ListInsert(LNode &L, int i, ElemType e){if(i>1)return false;if(i==1){//插入第一个结点的操作和其他结点的操作不同LNode *s = (LNode *) malloc (sizeof(LNode));s->data = e;s->next = L;L = s;//头指针指向新结点return true;}LNode *p;//指针p指向当前扫描到的结点int j = 1;//当前p指向的是第几个结点p = L;//p指向第1个结点(注意:不是头结点)while(p! = NULL && j < i-1){//循环找到第i-1个结点p = p->next;j++;}if(p == NULL)//i值不合法return false;LNode *s = (LNode *) malloc (sizeof(LNode));s->data = e;s->next = p->next;p->next = s;return true;//插入成功
}

代码分析:

  • i=1时,如果单链表不带头结点,则插入、删除第1个元素时,需要修改头指针L
  • i>1时,这种情况和带头结点的情况是一样的,唯一需要注意的是:我们修改了int j = 1

指定结点的后插操作

//后插:在p结点之后插入元素e
bool InsertNextNode(LNode *p, ElemType e){if(p == NULL)return false;LNode *s = (LNode *) malloc (sizeof(LNode));if(s == NULL)return false;s->data = e;//用结点s保存元素es->next = p->next;p->next = s;//将结点s连到p之后return true;
}

指定结点的前插操作

//前插:在p结点之前插入元素e
bool InsertPriorNode(LNode *p, ElemType e)

前插的困难在于,单链表只能往后查找结点,对于给定的结点p,之前有哪些结点都是未知的
那么如何操作呢?我们可以传入头指针

bool InsertPriorNode(LinkList L, LNode *p, ElemType e)

传入头指针之后,通过循环的方式查找p的前驱结点q,然后再对q进行后插操作,并且通过这种方式操作时,时间复杂度为 O ( n ) O(n) O(n)


但是还有另一种实现方式:

bool InsertPriorNode(LNode *p, ElemType e){if(p == NULL)return false;LNode *s = (LNode *) malloc (sizeof(LNode));if(s == NULL)return false;s->next = p->next;p->next = s;//新结点s连接到p之后s->data = p->data;//将p中的元素复制到s中p->data = e;//p中的元素覆盖为ereturn true;
}

通过这种方式的时间复杂度为 O ( 1 ) O(1) O(1)


王道书版本:

bool InsertPriorNode(LNode *p, LNode *s){if(p == NULL || s == NULL)return false;s->next = p->next;p->next = s;               //s连到p之后ElemType temp = p->data;   //交换数据部分p->data = s->data;s->data = temp;return true;
}

代码分析:

  1. p->data: 访问指针p所指向的结点的data成员
  2. ElemType temp: 声明一个类型为ElemType的局部变量tempElemType通常是一个自定义的类型(例如intfloat结构体等),它表示结点中存储的数据类型
  3. temp = p->data: 将p->data的值赋值给temp
ElemType temp = p->data; // 将 p 结点的 data 值存储到 temp 变量中
p->data = s->data;       // 将 s 结点的 data 值赋值给 p 结点的 data
s->data = temp;          // 将 temp 中保存的原 p 结点的 data 值赋值给 s 结点的 data

通过以上步骤,p和s之间的data值就被交换了,但结点本身的位置没有改变。这样做的目的是为了在逻辑上实现插入前驱结点的效果。

删除

按位序删除

只探讨带头结点的情况:
ListDelete(&L, i, &e)删除操作——删除表L中第i个位置的元素,并用e返回删除元素的值
找到第i-1个结点,将其指针指向第i+1个结点,并释放第i个结点
头结点可以看做“第0个”结点
具体代码实现:

bool ListDelete(LinkList &L, int i, ElemType &e){if(i<1)return false;LNode *p;                     //指针p指向当前扫描到的结点int j = 0;                    //当前p指向的是第几个结点p = L;                        //L指向头结点,头结点是第0个结点(不存数据)while(p != NULL && j < i-1){  //循环找到第 i-1 个结点p = p->next;j++;}if(p == NULL)                 //i值不合法return false;if(p->next == NULL)           //第 i-1 个结点后,已经没有其他结点return false;LNode *q = p->next;           //令q指向被删除的结点e = q->data;                  //用e返回元素的值p->next = q->next;            //将 *q 结点从链中断开free(q);                      //释放结点的存储空间return true;
}

代码分析:

  • 在这部分代码中:
    • LNode *q = p->next;: q 指向要删除的第 i 个结点
    • e = q->data;: 将被删除结点的数据存储在 e 中
    • p->next = q->next;: 这是关键的一步,将 pnext 指针指向 q 的后继结点,从而将 q 结点从链表中断开。具体来说,p->next 本来指向 q,现在改为指向 q 的后继结点 q->next
    • free(q);: 释放 q 结点的内存。
  • 我们来更详细地解释 p->next = q->next; 的意义:
    • 在这条语句之前,链表的链接是这样的:
      p --> q --> q->next
    • 执行这条语句后,链表的链接变成了这样:
      p -> q->next
    • 这样,q 结点就从链表中被移除了
  • 平均、最坏时间复杂度: O ( n ) O(n) O(n)最好时间复杂度: O ( 1 ) O(1) O(1)

指定结点的删除

//删除指定结点p
bool DeleteNode(LNode *p)

删除结点p,需要修改其前驱结点的next指针
解决方法:

  1. 传入头指针,循环寻找 p 的前驱结点
  2. 偷天换日(类似于结点前插的实现)
bool Delete(LNode *p){if(p == NULL)return false;LNode *q = p->next;       //令q指向*p的后继结点p->data = p->next->data;  //和后继结点交换数据域p->next = q->next;        //将*q结点从链中断开free(q);                  //释放后继结点的存储空间return true;
}

p->next指向 q 结点的后继结点(可能是NULL),这个时候 q 结点就被断开了,释放存储空间即可
时间复杂度: O ( 1 ) O(1) O(1)
但是,如果 p 是最后一个结点,这一段代码是有 bug 的。这种情况下,我们只能从头开始依次寻找 p 的前驱
时间复杂度: O ( 1 ) O(1) O(1)

单链表的局限性

无法逆向检索,有时候不太方便。如果有指向前面的指针,情况就不一样了
这种表就是双链表

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

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

相关文章

量子加速超级计算简介

本文转载自&#xff1a;量子加速超级计算简介(2024年 3月 13日) By Mark Wolf https://developer.nvidia.cn/zh-cn/blog/an-introduction-to-quantum-accelerated-supercomputing/ 文章目录 一、概述二、量子计算机的构建块&#xff1a;QPU 和量子位三、量子计算硬件和算法四、…

代码随想录算法训练营第三十七 | ● 738.单调递增的数字 ● 968.监控二叉树

738.单调递增的数字 讲解链接&#xff1a;https://programmercarl.com/0738.%E5%8D%95%E8%B0%83%E9%80%92%E5%A2%9E%E7%9A%84%E6%95%B0%E5%AD%97.html class Solution { public:int monotoneIncreasingDigits(int n) {//整数转字符串&#xff0c;变为字符串访问比诸位取出数字要…

项目集成过程中的makefile记录

项目集成过程中的makefile记录 文章目录 项目集成过程中的makefile记录1.基础概念注释打印赋值方式常用变量$ 伪目标函数wildcard 多目录、文件操作 2.思路梳理**需求分析**目录结构 3.可行示例 持续更新中1.基础概念 注释 # 示例&#xff1a; # 项目名称打印 echo "H…

控制台相关

输入输出 输出 Console.WriteLine("123123");//光标空行 Console.Write("123123123123");//不空行输入 string str Console.ReadLine(); //如果在ReadKey(true)不会把输入的内容显示在控制台上 char c Console.ReadKey(true).KeyChar; Console.WriteL…

ACM实训第25天

第四套 第一道&#xff08;修改&#xff09; #include<stdio.h> #include<string.h> int cnt[10]; void count_digits(int n,int* cnt){for(int i1;i<n;i){int numi;while(num){cnt[num%10];num/10;}} } int main(){int t;scanf("%d\n",&t);whi…

力扣刷题--2553. 分割数组中数字的数位【简单】

题目描述 给你一个正整数数组 nums &#xff0c;请你返回一个数组 answer &#xff0c;你需要将 nums 中每个整数进行数位分割后&#xff0c;按照 nums 中出现的 相同顺序 放入答案数组中。 对一个整数进行数位分割&#xff0c;指的是将整数各个数位按原本出现的顺序排列成数…

名为投资实为借贷,如何处理

投资近百万参与号称“高回报、零风险”的内部商铺投资项目&#xff0c;与公司签订商铺投资合同及租赁合同。本想投资商铺收取租金&#xff0c;没想到不仅租金没拿到手&#xff0c;连本金都要不回来。 2019年底&#xff0c;原告何某&#xff08;乙方&#xff09;与被告祁东县某…

QSettings注册表 json双模式配置文件

qt QSettings 类可用来保存软件设置&#xff0c;json文件也是保存软件设置的很好的方式&#xff0e; 这里结合json文件和QSettings注册表来保存软件设置&#xff0e;区别在于json文件在软件目录&#xff0c;每次更新时会被覆盖&#xff0c;注册表中设置持久有效&#xff0c;…

14.FreeRTOS 消息缓存 Message Buffer

FreeRTOS 消息缓存&#xff08;Message Buffer&#xff09;的使用 介绍 在实时操作系统&#xff08;RTOS&#xff09;中&#xff0c;任务之间的通信是一个非常重要的方面。FreeRTOS 提供了多种机制来实现任务间通信&#xff0c;其中之一就是消息缓存&#xff08;Message Buffe…

【IC验证】一文速通多通道数据整型器(MCDF)

目录 01 README 02 MCDF设计结构 2.1 功能描述 2.2 设计结构 2.3 接口与时序 2.3.1 系统信号接口 2.3.2 通道从端接口 2.3.3 整形器接口 2.3.4 控制寄存器接口 2.3.4.1 接口时序图 2.3.4.2 各数据位信息 03 验证框图 3.1 reg_pkg 3.1.1 reg_trans 3.1.2 reg_driv…

【一刷《剑指Offer》】面试题 27:二叉搜索树与双向链表

牛客对应题目链接&#xff1a;二叉搜索树与双向链表_牛客题霸_牛客网 (nowcoder.com) 力扣对应题目链接&#xff1a;LCR 155. 将二叉搜索树转化为排序的双向链表 - 力扣&#xff08;LeetCode&#xff09; 一、《剑指 Offer》对应内容 二、分析题目 上面力扣上的这道题目和牛客…

AGM DAP-LINK 离线烧录报错信息分析

DAP-LINK 支持离线烧录。 即&#xff1a;先把要烧录的bin 烧录到DAP-LINK 中&#xff1b;然后DAP-LINK 可以脱离PC&#xff0c;上电后通过按键对目标板进行烧录。 CMSIS-DAP模式 跳线JGND断开&#xff0c;状态LED D4快闪&#xff0c;D3常亮&#xff08;串口状态&#xff09;。…

deepin开发web前端:探索、挑战与无限可能

deepin开发web前端&#xff1a;探索、挑战与无限可能 在数字化浪潮汹涌的时代&#xff0c;Web前端作为连接用户与数字世界的桥梁&#xff0c;其重要性日益凸显。而deepin作为一款优秀的开源操作系统&#xff0c;为Web前端开发者提供了广阔的舞台。本文将围绕deepin开发Web前端…

接口设计的最佳实践-下篇

大多数程序员&#xff0c;做得最多的事&#xff0c;也不过是写接口这件事而已。 今天继续总结下接口设计需要注意的点。尽量每种都给出具体的场景、案例等&#xff0c;希望大家能有所收获。 1、接口幂等 幂等性&#xff1a;是指一个操作或者一个服务&#xff0c;无论执行多少…

小程序怎样进行本地存储的读、写、删、清?

小程序进行本地存储的读、写、删、清操作&#xff0c;可以通过微信小程序提供的API来实现。这些API分为同步和异步两种类型&#xff0c;以满足不同的使用场景和需求。 同步操作 同步操作会阻塞后续的代码执行&#xff0c;直到操作完成。 写入本地缓存&#xff08;写&#xf…

Vue3 - Mac系统用文本编辑写html不显示效果的坑

平时在win系统下&#xff0c;可以直接对文本进行编辑&#xff0c;非常的舒服。 在mac系统中&#xff0c;也有类似的功能&#xff0c;就是文本编辑&#xff0c;没想到居然还有坑。 这是我mac系统中创建的html文件&#xff0c;想着没有几行代码&#xff0c;就没有开编辑器了&am…

C语言 | Leetcode C语言题解之第125题验证回文串

题目&#xff1a; 题解&#xff1a; bool isalumn(char c) {return (c > a && c < z) || (c > A && c < Z) || (c > 0 && c < 9); }bool isPalindrome(char* s) {for (int left 0, right strlen(s) - 1; left < right; left, …

【数据库系统概论】事务

概述 在数据库系统中&#xff0c;事务&#xff08;Transaction&#xff09;是指一组作为单个逻辑工作单元执行的操作。这些操作要么全部成功&#xff08;提交&#xff09;&#xff0c;要么全部失败&#xff08;回滚&#xff09;。事务的主要目的是确保数据库的完整性和一致性&…

AI与NLP的完美结合:揭秘ChatGPT

AI与NLP的完美结合&#xff1a;揭秘ChatGPT 一、AI大模型的发展历程 AI大模型的发展可追溯到早期的深度学习技术。深度学习通过多层神经网络处理复杂的数据模式&#xff0c;显著提升了图像识别、语音识别等领域的性能。随后&#xff0c;研究人员将注意力转向NLP&#xff0c;开…

【传知代码】多视图3D目标检测位置嵌入变换(论文复现)

前言&#xff1a;三维目标检测技术正逐渐成为计算机视觉领域的重要研究方向。特别是在自动驾驶、增强现实&#xff08;AR&#xff09;、虚拟现实&#xff08;VR&#xff09;以及机器人导航等应用中&#xff0c;对三维空间内目标的精准检测与定位显得尤为重要。然而&#xff0c;…