双链表详解(初始化、插入、删除、遍历)(数据结构与算法)

1. 单链表与双链表的区别

单链表(Singly Linked List)和双链表(Doubly Linked List)是两种常见的链表数据结构,它们在节点之间的连接方式上有所区别。

单链表:

  1. 单链表的每个节点包含两个部分:数据域和指针域。
  2. 数据域存储节点的值或数据。
  3. 指针域存储指向下一个节点的指针,即单链表的指针指向下一个节点。
  4. 单链表只能从头节点开始顺序访问,通过指针域进行节点之间的连接。
  5. 单链表只能单向遍历,即只能从头节点开始,沿着指针域逐个访问下一个节点。
  6. 无法逆向检索,有时候不太方便

双链表:
7. 双链表的每个节点包含三个部分:数据域、指向前一个节点的指针和指向下一个节点的指针。
8. 数据域存储节点的值或数据。
9. 前驱指针指向前一个节点,后继指针指向下一个节点。
10. 双链表可以双向遍历,既能从头节点顺序遍历,也可以从尾节点逆序遍历。
11. 双链表相较于单链表需要额外存储指向前一个节点的指针,因此会在空间上占用更多的内存。
12. 可进可退,存储密度更低一点

单链表和双链表的选择取决于需要解决的问题和应用场景。对于只需要顺序遍历或仅从头部开始操作的情况,单链表可能是更简洁和高效的选择。但对于需要在两个方向上遍历或在任意位置插入或删除节点的情况,双链表就更有优势了。

双链表: 初始化、插入、删除、遍历


2. 双链表的初始化(带头结点)

//初始化双链表 
typedef struct DNode		//定义双链表结点类型
{Elemtype data;			//数据域struct DNode *prior, *next;  //前驱和后继指针
}DNode, *DLinkList;	bool InitDLinkList(DLinkList &L)
{L = (DNode *) malloc(sizeof(DNode));		//分配一个头结点if(L == NULL)		//内存不足,分配失败return false;L->prior = NULL;		//头结点的prior永远指向NULLL->next = NULL;			//头结点之后暂时还没有结点return true;	
}
void testDLinkList()
{DLinkList L; //初始化双链表InitDLinkList(L);//后续代码......
}//判断双链表是否为空(带头结点)
bool Empty(DLinkList L) 
{	if(L->next == NULL)return true;elsereturn false;
}

3. 双链表的插入

在双链表中插入节点需要更新前驱节点和后继节点的指针连接,操作相对比较复杂。

  1. 首先,创建一个新节点,并设置它的数据值。
  2. 找到要插入位置的前驱节点。
  3. 将新节点的前驱指针指向前驱节点。
  4. 将新节点的后继指针指向前驱节点的后继节点。
  5. 更新前驱节点的后继指针指向新节点。
  6. 如果新节点的后继节点非空,将后继节点的前驱指针指向新节点。
  • 若结点在双链表插入数据元素e,且p结点有后继结点,如下图所示。
    在这里插入图片描述
    程序设计如下:
//在p结点之后插入s结点
bool InsertNextDNode(DNode *P, DNode *s)
{s->next = p->next;	//将结点*s插入到结点*p之后, 如上图步骤1p->next->prior = s;  //如上图步骤2s->prior = p;     //如上图步骤3p->next = s;      //如上图步骤4
}

在这里插入图片描述

  • 如果p是最后一个结点,会发生什么?

p->next->prior = s; //如上图步骤2 这一行代码将出现问题 p->next指向的是NULL, 改进如下:

在这里插入图片描述
程序设计如下:注意修改指针时要注意顺序!!!

//在p结点之后插入s结点
bool InsertNextDNode(DNode *p, DNode *s)
{if (p == NULL || s == NULL) //非法参数return false;s->next = p->next;        //  (*)if(p->next != NULL){p->next->prior = s;  //如果p结点有后继结点,就回到了上一个情况}s->prior = p;	  //蓝色箭头操作p->next = s;      //橙色箭头操作    (**)    return true;  //插入成功
}
  • 指针顺序不能错! 假如 (*) 与(**)行交换。 那么就会出现如下图情况,p指针next与s->next指向同一位置。
    在这里插入图片描述
    用后插操作实现结点插入有什么好处?
  • 按位序插入前插操作:效果等同于 ====> 可以先找到前驱结点,再对其做后插操作
  • 其他插入操作,都可以转换成后插操作

在这里插入图片描述


4. 双链表的删除

在双链表中删除节点的操作相对比较复杂,因为我们需要维护前驱节点和后继节点之间的指针连接。
以下是删除双链表中某个节点的一般步骤:

  1. 首先,找到要删除的节点。
  2. 如果要删除的节点是头节点,将头节点指针指向下一个节点,并更新下一个节点的前驱指针为 nullptr。
  3. 如果要删除的节点是尾节点,将前一个节点的后继指针设为 nullptr,并更新尾节点指针为前一个节点。
  4. 如果要删除的节点既不是头节点也不是尾节点,将前一个节点的后继指针指向要删除节点的后一个节点,将后一个节点的前驱指针指向要删除节点的前一个节点。
  5. 释放要删除的节点的内存空间。
//删除p的后继结点q, 如下图所示
p->next = q->next;
q->next->prior = p;
free(q);

在这里插入图片描述


在实际使用中,应该确保要删除的节点在链表中确实存在。如果删除的节点不存在于链表中,需要根据具体的需求进行错误处理。同时,删除节点后必须确保释放相应的内存空间,以防止内存泄漏问题的发生。

删除q结点

在这里插入图片描述

将 p 结点的next指针,指向q结点的后继结点。

在这里插入图片描述

释放 q 结点空间

在这里插入图片描述

3.1 删除p结点的后继结点

//删除p结点的后继结点
bool DeleteNextDNode(DNode *p)
{if(p==NULL)		return false;DNode *q = p->next;			//找到p的后继结点if(q==NULL)return false;		//p没有后继结点p->next = q->next;if(q->next != NULL)		//q结点不是最后一个结点q->next->prior = p;	free(q);		//释放结点空间return true;	//删除成功	
}

3.2 销毁一个双链表

//销毁一个双链表
void DestoryList(DLinkList &L)
{//循环释放各个数据结点while(L->next != NULL)    //判断头结点是否有后继结点,直到头结点后再无其他结点结束循环{DeleteNextDNode(L);    //删除p结点的后继结点}free(L);	//释放头结点L = NULL;		//头指针指向NULL	
}

5. 双链表的遍历

4.1 后向遍历

while(p != NULL)
{//对结点p做相应处理,如打印p = p->next;
}

4.2 前向遍历

while(p != NULL)
{//对结点p做响应处理p = p->prior;
}

4.3 前向遍历(跳过头结点)

while(p->prior != NULL)  //当前驱为NULL时,此时遍历到第一个结点,退出循环,跳过了头结点。
{//对结点p做相应处理p = p->prior;
}

双链表不可随机存取,按位查找、按值查找操作都只能用遍历的方式实现,时间复杂度为O(n)


6. 知识点回顾

在这里插入图片描述

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

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

相关文章

小程序使用echarts(超详细教程)

小程序使用echarts第一步就是先引用到小程序里面,可以直接从这里下载 文件很多,我们值下载 ec-canvas 就好,下载完成后,直接放在pages同级目录下 index.js 在我们需要的页面的 js 文件顶部引入 // pages/index/index.js impor…

【小白专用】微信小程序个人中心、我的界面(示例一)23.11.04

微信小程序使用button按钮实现个人中心、我的界面(示例一) 微信小程序个人中心、我的界面,使用button按钮实现界面布局,更好的将分享好友、获取头像等功能展现出来,更多示例界面,请前往我的主页哦。 1、js…

ChatGPT 实际上是如何工作的?

添加图片注释,不超过 140 字(可选) ChatGPT 操作的两个主要阶段 我们再用谷歌来打个比方。当你要求谷歌查找某些内容时,你可能知道它不会——在你提出要求的那一刻——出去搜索整个网络来寻找答案。相反,谷歌会在其数…

COCOS2DX3.17.2 Android升级targetSDK30问题解决方案

一、luajit不兼容问题 不兼容版本:【2.1.0-bate2、2.1.0-bate3都存在异常】 出问题系统:Android11;Android10的系统部分机型有问题,部分机型正常 异常点1:c调用lua接口,pushObjiect的时候crash 异常点2…

下载JMeter

最近准备对接口进行测试,下了JMeter来玩玩 一、下载地址 百度云下载:链接:https://pan.baidu.com/s/1RbMemwzGR6dnDi6BSlPGrw 提取码:uscy 二、开启方式 1.解压后打开目录 2. 进入bin目录 3.双击 jmeter.bat,就可以…

1015. 摘花生

题目&#xff1a; 1015. 摘花生 - AcWing题库 思路&#xff1a;dp 代码&#xff1a; #include<iostream> #include<cstdio> #include<cmath> using namespace std; const int N 110; typedef long long ll; int T, r, c; int num[N][N]; ll dp[N][N];//dp…

Linux学习第27天:Platform设备驱动开发(一): 专注与分散

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 专注与分散是我在题目中着重说明的一个内容。这是今天我们要学习分离与分层概念的延伸。专注是说我们要专注某层驱动的开发&#xff0c;而对于其他层则是芯片厂商…

基于LDA主题+协同过滤+矩阵分解算法的智能电影推荐系统——机器学习算法应用(含python、JavaScript工程源码)+MovieLens数据集(三)

目录 前言总体设计系统整体结构图系统流程图 运行环境模块实现1. 数据爬取及处理2. 模型训练及保存1&#xff09;协同过滤2&#xff09;矩阵分解3&#xff09;LDA主题模型 3. 接口实现1&#xff09;流行电影推荐2&#xff09;相邻用户推荐3&#xff09;相似内容推荐 相关其它博…

ubuntu 20.04 + cuda-11.8 + cudnn-8.6+TensorRT-8.6

1、装显卡驱动 ubuntu20.04 cuda10.0 cudnn7.6.4_我是谁&#xff1f;&#xff1f;的博客-CSDN博客 查看支持的驱动版本&#xff1a; 查看本机显卡能够配置的驱动信息 luhost:/usr/local$ ubuntu-drivers devices/sys/devices/pci0000:00/0000:00:01.0/0000:01:00.0 moda…

LeetCode | 203. 移除链表元素

LeetCode | 203. 移除链表元素 OJ链接 这里有两个思路我接下来看 当cur不等于6就一直找&#xff0c;找到了6就删除&#xff0c;但是能不能直接删除&#xff1f;不能&#xff0c;直接free了就找不到下一个了 这个时候我们就要定义next指针&#xff0c;和prev指针&#xff0c…

必须收藏:IPv6核心知识梳理!!(原理+基础配置)

一、概述 由于NAT技术的应用&#xff0c;缓解了IPv4地址不足产生的问题&#xff0c;但是部署IPv6是解决IPv4地址不足的最终方案。当前世界上不同地区对部署IPv6的需求强烈程度不一&#xff0c;且当前IPv4网络仍然占主流地位&#xff0c;因此短时间内IPv6和IPv4将会共存。 IPv4网…

【Midjourney入门教程3】写好prompt常用的参数

文章目录 1、图片描述词&#xff08;图片链接&#xff09;文字描述词后缀参数2、权重划分3、后缀参数版本选择&#xff1a;--v版本风格&#xff1a;--style长宽比&#xff1a;--ar多样性: --c二次元化&#xff1a;--niji排除内容&#xff1a;--no--stylize--seed--tile、--q 4、…

Spring Data Redis + RabbitMQ - 基于 string + hash 实现缓存,计数(高内聚)

目录 一、Spring Data Redis 1.1、缓存功能(分析) 1.2、案例实现 一、Spring Data Redis 1.1、缓存功能(分析) hash 类型存储缓存相比于 string 类型就有更多的更合适的使用场景. 例如,我有以下这样一个 UserInfo 信息 假设这样一个场景就是:万一只想获取其中某一个…

机器视觉行业最大的污点是什么?99%机器视觉公司存在测量项目数据造假,很遗憾,本人不没有恪守技术的本分

机器视觉行业最大的污点是什么&#xff1f;99%机器视觉公司存在测量项目数据造假&#xff0c;很遗憾&#xff0c;本人没有恪守技术的本分。 1%是没做过机器视觉测量项目&#xff0c;我们应该具体分析和具体判断&#xff0c;更应该提高自己的认知能力和技术能力。 那我们​在现场…

根据Word模板,使用POI生成文档

突然想起来有个小作业&#xff1a;需要根据提供的Word模板填充数据。这里使用POI写了一个小demo验证下。 测试用模板&#xff1a; 执行结果 1.引入依赖坐标 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId&…

【kafka】记一次kafka基于linux的原生命令的使用

环境是linux&#xff0c;4台机器&#xff0c;版本3.6&#xff0c;kafka安装在node 1 2 3 上&#xff0c;zookeeper安装在node2 3 4上。 安装好kafka&#xff0c;进入bin目录&#xff0c;可以看到有很多sh文件&#xff0c;是我们执行命令的基础。 启动kafka&#xff0c;下面的…

人工智能师求职面试笔试题及答案汇总

人工智能师求职面试笔试题及答案汇总 1.如何在Python中实现一个生成器&#xff1f; 答&#xff1a;在Python中&#xff0c;生成器是一种特殊类型的迭代器。生成器允许你在需要时才生成值&#xff0c;从而节省内存。生成器函数在Python中是通过关键字yield来实现的。例如&…

Opencascad(C++)-创建自定义坐标系

文章目录 1、前言2、在Opencascad中显示小的坐标系3、在Opencascad中创建自定义的坐标系 1、前言 在Opencascad开发时&#xff0c;在view中可以显示小的坐标系&#xff0c;但是有时我们需要在建模时创建基准坐标系&#xff0c;当然可以作为工件坐标系也可以作为基准坐标系。本…

Hive【Hive(八)自定义函数】

自定义函数用的最多的是单行函数&#xff0c;所以这里只介绍自定义单行函数。 Coding 导入依赖 <dependency><groupId>org.apache.hive</groupId><artifactId>hive-exec</artifactId><version>3.1.3</version></dependency>…

Windows 开启 Kerberos 的火狐 Firefox 浏览器访问yarn、hdfs

背景&#xff1a;类型为IPA或者MIT KDC&#xff0c;windows目前只支持 firefoxMIT Kerberos客户端的形式&#xff0c;其他windows端浏览器IE、chrome、edge&#xff0c;没有办法去调用MIT Kerberos Windows客户端的GSSAPI验证方式&#xff0c;所以均无法使用 Windows 开启 Kerb…