单链表的定义、插入和删除

一、定义一个单链表
struct LNode{          //定义单链表节点类型ElemType data;     //存放节点数据元素struct LNode *next; //指针指向下一个结点
};
//增加一个新节点:在内存中申请一个结点所需空间,并用指针p指向这个结点
struct LNode * p =(struct LNode *)malloc(sizeof(struct LNode));

typedef 关键字 —— 数据类型重命名,所以上述代码还可以写成:

typedef struct LNode LNode; //struct LNode重命名为LNode
LNode * p =(LNode *)malloc(sizeof(LNode));

书本上的写法

typedef struct LNode{ElemType data;struct LNode *next;
}LNode,*LinkList;/*
上述写法与下面写法等价
*/
struct LNode{ElemType data;struct LNode *next;
};
typedef struct LNode LNode;
typedef struct LNode *LinkList;

这里有一点需要注意:
虽然LNode *和LinkList效果一样,但是两者的意义不一样。

强调这是一个单链表——LinkList
强调这是一个结点——LNode *

在这里插入图片描述

二、初始化一个单链表
1.带头结点

带头结点:头指针指向一个节点,这个结点不存放数据,这个结点的next存放指向第一个数据的指针,这样的节点叫头结点。

不带头结点:头指针指向的第一个节点就是数据元素结点。

在这里插入图片描述

typedef struct LNode{ElemType data;struct LNode *next;
}LNode,*LinkList;
//初始化一个单链表(带头结点)
bool InitList(LinkList &L){L = (LNode *)malloc(sizeof(LNode));//分配一个头结点if(L==NULL)        //内存不足,分配失败return false;L->next = null;    //头结点之后暂时还没有节点return true;
}
//判断单链表是否为空(带头结点)
bool Empty(LinkList L){if(L->next == NULL)return true;elsereturn false;
}
void test(){LinkList L; //声明一个指向单链表的指针InitList(L);//初始化一个空表//......
}
2.不带头结点

空表判断条件

L == NULL
三、单链表的插入

(一)按位序插入(带头结点)

typedef struct LNode{ElemType data;struct LNode *next;
}LNode,*LinkList;//在第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个结点(不存放数据)/*因为要插入到第i个结点,所以我们只要找到第i-1个结点,然后将结点插入				到第i-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; //插入成功}

代码解析:
1.j=0,以i=1为例,不满足j<i-1,直接跳过循环。

  • 第一步,LNode *s=(LNode *)malloc(sizeof(LNode)); 开辟一块结点内存空间;将e赋给s结点的data域:s->data = e在这里插入图片描述
  • 第二步,改变指针指向:s->next = p->next; p->next = s;
    需要注意的是①和②的操作不能颠倒,否则就会导致s结点之后的数据丢失。
    在这里插入图片描述
    2.j=0,以i=3为例,i-1=2j<i-1,满足循环条件
  • 下面分析一下循环部分的代码
    j=0j<2,执行p=p->next,满足循环条件,p向后移动
    在这里插入图片描述
    j++=1j<2,执行p=p->next,满足循环条件,p向后移动
    在这里插入图片描述
    ③j++=2,不满足循环条件,跳出循环

之后插入的操作跟上述i=1的操作一样

  • 开辟结点空间,并给结点空间赋值:LNode *s=(LNode *)malloc(sizeof(LNode)); s->data = e;
    在这里插入图片描述
  • 改变指针指向:s->next = p->next; p->next = s;
    在这里插入图片描述
    3.如果i=6,当p指向第5个结点时不满足p!=null(即找不到第i-1个结点),会跳出循环

时间复杂度分析:

  • 插入表头时间复杂度为 O(1)
  • 插入到表尾时间复杂度为O(n)
  • 平均时间复杂度为O(n)

(二)按位序插入(不带头结点)
在这里插入图片描述

思路:跟带头结点一样,找到第i-1个结点,然后把结点插入第i-1个结点之后。
需要注意的是,如果不带头结点,则插入、删除第一个元素时,需要更改头指针。

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

四、指定结点的插入操作

(一)指定结点的后插操作

//后插操作:在p结点之后插入元素e
bool InsertNextNode(LNode *p,ElemType e){if(p==NULL)return false;s->data = e;s->next = p->next;p->next = s;return true;
}

(二)指定结点的前插操作
思路1:如果要在p的前驱插入一个结点,那就循环查找p的前驱q,再对q后插
但是如果要找到p的前驱就要传入头指针。

//前插操作:在p结点之前插入元素e
bool InsertPriorNode(LinkList L,LNode *p,ElemType E){
}

时间复杂度:O(n)
思路2:
新建一个结点,让这个结点作为p的后继结点。然后调换一下两个结点里的数据,这样需要插入的数据依旧在p的前面,依然可以实现前插操作。

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)

关键代码图解:

s->next=p->next;p->next=s;

在这里插入图片描述
s->data=p->data; p->data=e;
在这里插入图片描述

五、删除

(一)、按位序删除

删除表L中第i个位置的元素

思路:如果要删除第i个元素,那么找到第i-1个元素,让其指向第i+1个元素,再把第i个元素的空间释放掉。

//带头结点
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;  //被删除的结点从链表中断开free(q);          //释放结点的存储空间return true;      //删除成功
}

最坏、平均时间复杂度:O(n)
最好时间复杂度:O(1)

(二)、指定结点删除

同样的道理,如果不传入头指针,那就找不到指定指定结点的前驱结点。

那如果在不传入头指针的情况下该怎么删除呢?我们想到一个办法:把p的后继结点的数据域赋给p,然后再把p的后继结点q所指向的空间释放掉。

//删除指定结点p
bool DeleteNode(LNode *p){if(p==NULL)return false;LNode *q=p->next;//q指向p的后继结点p->data=p->next->data;//和后继结点交换数据域p->next=q->next;free(q);return true;}

①初始状态,LNode *q=p->next;
在这里插入图片描述
②交换数据域,p->data=p->next->data;
在这里插入图片描述
③改变指针指向,p->next=q->next;,最后再释放q所指向的空间,free(q);
在这里插入图片描述
注意:如果要删除的是最后一个结点的话,q会指向null,这时候只能采用传入头指针遍历,找到前驱结点的办法来删除。

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

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

相关文章

Nextjs官方文档异疑惑

第一个区别&#xff1a;不同的页面对应的路由器设定&#xff01; 继续用 app 路由器&#xff08;推荐&#xff0c;Next.js 未来主流&#xff09; 路由规则&#xff1a;app 目录下&#xff0c;文件夹 page.tsx 对应路由。例如&#xff1a; app/page.tsx → 对应 / 路由&#xf…

突破AI模型访问的“光标牢笼”:长上下文处理与智能环境隔离实战

> 当AI模型面对浩瀚文档却只能处理零星片段,当关键信息散落各处而模型“视而不见”,我们该如何打破这堵无形的墙? 在自然语言处理领域,**输入长度限制**(常被称为“光标区域限制”)如同一个无形的牢笼,严重制约了大型语言模型(LLM)在真实场景中的应用潜力。无论是分…

AI 智能质检系统在汽车制造企业的应用​

某知名汽车制造企业在其庞大且复杂的生产流程中&#xff0c;正面临着棘手的汽车零部件质检难题。传统的人工质检方式&#xff0c;完全依赖人工的肉眼观察与简单工具测量。质检员们长时间处于高强度的工作状态&#xff0c;精神高度集中&#xff0c;即便如此&#xff0c;由于人工…

设计模式》》门面模式 适配器模式 区别

// 复杂子系统 class CPU {start() { console.log("CPU启动"); } } class Memory {load() { console.log("内存加载"); } } class HardDrive {read() { console.log("硬盘读取"); } }// 门面 class ComputerFacade {constructor() {this.cpu ne…

windows内核研究(驱动开发 第一个驱动程序和调试环境搭建)

驱动开发 第一个驱动程序 驱动的开发流程 1.编写代码 -> 生成.sys文件 -> 部署 -> 启动 -> 停止 ->卸载 // 编写我们的第一个驱动程序 #include<ntddk.h>// 卸载函数 VOID DrvUnload(PDRIVER_OBJECT DriverObject) {DbgPrint("我被卸载了\n"…

ABP VNext + 多级缓存架构:本地 + Redis + CDN

ABP VNext 多级缓存架构&#xff1a;本地 Redis CDN &#x1f4da; 目录ABP VNext 多级缓存架构&#xff1a;本地 Redis CDN一、引言 &#x1f680;二、环境与依赖 &#x1f6e0;️三、架构概览 &#x1f310;请求全链路示意 &#x1f6e3;️四、本地内存缓存层 &#x1…

RGBA图片格式转换为RGB格式(解决convert转换的失真问题)

使用convert转换的问题 OpenCV 的 cv2.cvtColor(…, cv2.COLOR_BGRA2GRAY) 会直接忽略 Alpha 通道的含义&#xff0c;将它当作第四个颜色通道来处理。 转换公式如下&#xff1a; gray 0.114*255 0.587*0 0.299*0 ≈ 29也就是说&#xff0c;即使 Alpha 为 0&#xff08;完全透…

Spring AI之Prompt开发

文章目录1 提示词工程1_核心策略2_减少模型“幻觉”的技巧2 提示词攻击防范1_提示注入&#xff08;Prompt Injection&#xff09;2_越狱攻击&#xff08;Jailbreaking&#xff09;3 数据泄露攻击&#xff08;Data Extraction&#xff09;4 模型欺骗&#xff08;Model Manipulat…

Java面试(基础篇) - 第二篇!

未看第一篇的&#xff0c;这里可以直达 Java面试(基础篇) - 第一篇 Integer对象可以用判断吗&#xff1f;为什么&#xff1f; 回答 不可以&#xff0c;因为 比较的是对象的实例&#xff08;内存地址&#xff09;&#xff0c;Integer是有一个缓存机制的&#xff0c;它会将-1…

【C# in .NET】11. 探秘泛型:类型参数化革命

探秘泛型:类型参数化革命 泛型是 C# 和.NET框架中一项革命性的特性,它实现了 “编写一次,多处复用” 的抽象能力,同时保持了静态类型的安全性和高性能。与 C++ 模板等其他语言的泛型机制不同,.NET 泛型在 CLR(公共语言运行时)层面提供原生支持,这使得它兼具灵活性、安…

菜单权限管理

菜单管理系统的整体架构1.Menu 菜单表2.role 角色表3.role_menu 角色菜 单关联表&#xff08;多对多 &#xff09;要找role_id为3的角色能用哪个菜单:SELECT *FROM sys_menu a LEFT JOIN sys_role_menu b ON a.menu_id b.menu_id WHERE role_id3拆分开就是4.user 用户表5.user…

SQL FOREIGN KEY:详解及其在数据库设计中的应用

SQL FOREIGN KEY:详解及其在数据库设计中的应用 引言 在数据库设计中,数据完整性是至关重要的。SQL FOREIGN KEY(外键)是实现数据完整性的一种有效手段。本文将详细解释SQL FOREIGN KEY的概念、用途以及在实际数据库设计中的应用。 外键概述 1. 定义 外键(FOREIGN KE…

[yotroy.cool] 记一次 spring boot 项目宝塔面板部署踩坑

个人博客https://www.yotroy.cool/&#xff0c;感谢关注&#xff5e; 图片资源可能显示不全&#xff0c;请前往博客查看哦&#xff01;部署了个新项目&#xff0c;给我整抑郁了。。。下面是踩坑过程 宝塔面板 MySql5.7 版本 root 密码错误 这个MySQL5.7 安装完后就跑不了&#…

前端之HTML学习

HTML 学习笔记 前端三大件 HTML&#xff1a;超文本标记语言&#xff08;HyperText Markup Language&#xff09;CSS&#xff1a;层叠样式表JavaScript&#xff1a;客户端脚本语言常用框架&#xff1a;jQuery Vue 3(Element plus) HTML 基本概念 超文本&#xff1a;包含图像…

迅速高效从web2到web3转型 ,开启远程工作

Web2向Web3的转型&#xff0c;是技术、产品、组织结构和商业模式的深度变革。若要迅速且高效地完成这个转型&#xff0c;需要清晰的路径规划和战略执行。 目录 &#x1f501; 一、理解核心区别&#xff1a;Web2 vs Web3 &#x1f680; 二、转型路径 1. 选择合适的切入点 …

区块链开发协作工具全景图:从智能合约管理到去中心化治理

&#x1f4a5; 三重绞索&#xff1a;区块链开发的至暗时刻 1. 版本管理的深渊 当某DeFi团队凌晨修复漏洞时&#xff0c;发现生产环境运行的竟是两周前的废弃分支——37%的项目因代码分支混乱引发生产事故&#xff08;Electric Capital 2024&#xff09;。智能合约的版本漂移如同…

冒泡排序、选择排序、插入排序、快速排序

目录 1. 冒泡排序 (Bubble Sort) 算法思路分析 代码实现 复杂度分析 2. 选择排序 (Selection Sort) 算法思路分析 代码实现 复杂度分析 3. 插入排序 (Insertion Sort) 算法思路分析 代码实现 复杂度分析 4. 快速排序 (Quick Sort) 算法思路分析 代码实现 复杂度…

PHP语言基础知识(超详细)第一节

一. PHP简介: PHP即“超文本预处理器”,创建于1994年,是一种通用开源脚本语言。PHP是在服务器端执行的脚本语言,与C语言类似,是常用的网站编程语言。PHP独特的语法混合了C、Java、Perl以及 PHP 自创的语法。利于学习,使用广泛,主要适用于Web开发领域。 二. PHP的优点:…

Reloaded-II项目:解决GitHub下载Mod缺少DLL文件的问题

Reloaded-II项目&#xff1a;解决GitHub下载Mod缺少DLL文件的问题 问题现象分析 在使用Reloaded-II项目加载从GitHub下载的"Debug Stuff"模组时&#xff0c;用户遇到了一个常见的技术问题&#xff1a;系统提示缺少DLL文件&#xff0c;导致模组无法正常运行。这种情况…

0-1搭建springboot+vue的教务管理系统(核心源码)

目录 后端核心代码&#xff1a; control层 service 层 mapper层 后端核心代码&#xff1a; control层&#xff1a; classControlsImpl package com.itheima.controls.impl;import com.itheima.mapper.ClassMapper; import com.itheima.pojo.Clazz; import com.itheima.po…