线性表——双向链表

  • 1. 双向链表的实现
    • 1.1 简单图例
    • 1.2 结点的定义
    • 1.3 新结点的创建
    • 1.4 链表的初始化
    • 1.5 结点的插入
      • 1.5.1 头部插入(头插)
      • 1.5.2 尾部插入(尾插)
      • 1.5.3 任意位置(前)插入
    • 1.6 结点的删除
      • 1.6.1 头部删除(头删)
      • 1.6.2 尾部删除(尾删)
      • 1.6.3 任意位置删除
    • 1.7 查找结点
    • 1.8 链表的打印
    • 1.9 链表的销毁
  • 2. 功能综合
  • 3. 误区纠正
  • 4. 正确使用链表

  到目前为止,我们对顺序表,链表都进行了初步是实现,其中仅完成了单链表。要知道,链表可不仅仅用是否有头结点来区分,另外还有单向、双向循环和非循环。将这些情况组合起来可以组成 8 种不同的链表,由于带头双向循环链表的使用情况较多,本章就对带头双向循环链表进行实现。

1. 双向链表的实现

1.1 简单图例

带头双向链表简单图例

1.2 结点的定义

typedef int LTDataType;
typedef struct ListNode
{LTDataType val;			//数据域struct ListNode* prev;	//指针域(存放前驱结点的地址)struct ListNode* next;	//指针域(存放后继结点的地址)
}ListNode;

注意:双向链表有 两个 指针域,一个指向前驱 结点,一个指向后继 结点。

1.3 新结点的创建

ListNode* CreateListNode(LTDataType x) {						//x为新结点的val部分(数据域)ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));	//为新结点开辟新空间if (newnode == NULL) {perror("malloc fail!");return;}newnode->val = x;newnode->next = NULL;newnode->prev = NULL;return newnode;				//返回新结点的地址
}

1.4 链表的初始化

  这里链表的初始化只是针对于头结点,相当于给头结点一个初始的值。

ListNode* DListInit() {ListNode* phead = CreateListNode(-1);	//给头结点的值赋为 -1phead->next = phead;phead->prev = phead;	//指针域全部指向本身(初始化)return phead;
}

测试时即可使用:

ListNode* Dlist=DListInit();

1.5 结点的插入

1.5.1 头部插入(头插)

注意:头部插入不是在头结点之前插入,而是在 头结点后面第一个结点之前插入

void DListPushFront(ListNode* phead, LTDataType x) {assert(phead);ListNode* newnode = CreateListNode(x);ListNode* cur = phead->next;phead->next = newnode;newnode->prev = phead;newnode->next = cur;cur->prev = newnode;
}

图解:
头插

1.5.2 尾部插入(尾插)

void DListPushBack(ListNode* phead, LTDataType x) {assert(phead);ListNode* tail = phead->prev;ListNode* newnode = CreateListNode(x);tail->next = newnode;newnode->prev = tail;newnode->next = phead;phead->prev = newnode;}

图解:
尾插

1.5.3 任意位置(前)插入

  在这部分功能上双向链表就比单链表好实现得多,双向链表自带一个前驱结点的地址,不需要像单链表一样从头开始遍历。

void DListInsert(ListNode* pos, LTDataType x) {assert(pos);	//判断pos不为空ListNode* newnode = CreateListNode(x);ListNode* cur = pos->prev;cur->next = newnode;newnode->prev = cur;newnode->next = pos;pos->prev = newnode;}

此部分图例可参考头插(同原理)

1.6 结点的删除

1.6.1 头部删除(头删)

void DListPopFront(ListNode* phead) {assert(phead);ListNode* front = phead->next;ListNode* back = front->next;phead->next = back;back->prev = phead;free(front);front = NULL;
}

图解:
头删

1.6.2 尾部删除(尾删)

  删除尾部结点只需要将倒数第二个的结点的位置找到,尾部结点的空间就可以直接释放了,接下来就直接链接头结点。

void DListPopBack(ListNode* phead) {assert(phead);ListNode* tail = phead->prev;ListNode* tail_prev = tail->prev;free(tail);tail = NULL;tail_prev->next = phead;phead->prev = tail_prev;}

图解:
尾删

1.6.3 任意位置删除

void DListErase(ListNode* pos) {assert(pos);ListNode* cur = pos->prev;ListNode* back = pos->next;cur->next = back;back->prev = cur;free(pos);pos = NULL;
}

此处图例可参考头删 尾删(同原理)

1.7 查找结点

  在顺序表中,当结点的位置被找到时,函数会返回顺序表的下标。在链表中,返回的就是结点的地址。

ListNode* DListFind(ListNode* phead, LTDataType x) {assert(phead);ListNode* cur = phead->next;while (cur != phead) {if (cur->val == x) {return cur;}cur = cur->next;}return NULL;	//找不到对应的结点返回NULL
}

1.8 链表的打印

void DListPrint(ListNode* phead) {assert(phead);ListNode* cur = phead->next;printf("(头结点)<->");while (cur!=phead) {printf("[%d]<->", cur->val);cur = cur->next;}printf("(头结点)\n");
}

打印部分可自行美化

1.9 链表的销毁

void DListDestory(ListNode* phead) {assert(phead);ListNode* cur = phead->next;while (cur != phead) {ListNode* Next = cur->next;		//先存储下一个结点的地址free(cur);						//再释放当前结点cur = Next;						//然后把Next给cur}free(phead);phead = NULL;
}

2. 功能综合

  功能整合起来代码较长,另外也可以通过接口的方式实现(层次分明),有不懂的可参考上文图解自行消化,main部分可以自行调用函数进行测试。

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>//创建新结点
ListNode* CreateListNode(LTDataType x) {						//x为新结点的val部分(数据域)ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));	//为新结点开辟新空间if (newnode == NULL) {perror("malloc fail!");return;}newnode->val = x;newnode->next = NULL;newnode->prev = NULL;return newnode;				//返回新结点的地址
}//初始化头结点
ListNode* DListInit() {ListNode* phead = CreateListNode(-1);	//给头结点的值赋为 -1phead->next = phead;phead->prev = phead;	//指针域全部指向本身(初始化)return phead;
}//头插
void DListPushFront(ListNode* phead, LTDataType x) {assert(phead);ListNode* newnode = CreateListNode(x);ListNode* cur = phead->next;phead->next = newnode;newnode->prev = phead;newnode->next = cur;cur->prev = newnode;
}//尾插
void DListPushBack(ListNode* phead, LTDataType x) {assert(phead);ListNode* tail = phead->prev;ListNode* newnode = CreateListNode(x);tail->next = newnode;newnode->prev = tail;newnode->next = phead;phead->prev = newnode;}//任意位置(前)插入
void DListInsert(ListNode* pos, LTDataType x) {assert(pos);	//判断pos不为空ListNode* newnode = CreateListNode(x);ListNode* cur = pos->prev;cur->next = newnode;newnode->prev = cur;newnode->next = pos;pos->prev = newnode;}//头删
void DListPopFront(ListNode* phead) {assert(phead);ListNode* front = phead->next;ListNode* back = front->next;phead->next = back;back->prev = phead;free(front);front = NULL;
}//尾删
void DListPopBack(ListNode* phead) {assert(phead);ListNode* tail = phead->prev;ListNode* tail_prev = tail->prev;free(tail);tail = NULL;tail_prev->next = phead;phead->prev = tail_prev;}//任意位置删除
void DListErase(ListNode* pos) {assert(pos);ListNode* cur = pos->prev;ListNode* back = pos->next;cur->next = back;back->prev = cur;free(pos);pos = NULL;
}//查找结点
ListNode* DListFind(ListNode* phead, LTDataType x) {assert(phead);ListNode* cur = phead->next;while (cur != phead) {if (cur->val == x) {return cur;}cur = cur->next;}return NULL;	//找不到对应的结点返回NULL
}//打印链表
void DListPrint(ListNode* phead) {assert(phead);ListNode* cur = phead->next;printf("(头结点)<->");while (cur!=phead) {printf("[%d]<->", cur->val);cur = cur->next;}printf("(头结点)\n");
}//销毁链表
void DListDestory(ListNode* phead) {assert(phead);ListNode* cur = phead->next;while (cur != phead) {ListNode* Next = cur->next;		//先存储下一个结点的地址free(cur);						//再释放当前结点cur = Next;						//然后把Next给cur}free(phead);phead = NULL;
}int main() {ListNode* Dlist = DListInit();  //初始化DListPushBack(Dlist, 1);DListPushBack(Dlist, 2);DListPushBack(Dlist, 3);DListPushBack(Dlist, 5);DListPushBack(Dlist, 4);		//尾插DListPrint(Dlist);				//打印DListPopBack(Dlist);			//尾删DListPrint(Dlist);				//打印DListPushFront(Dlist, 99);DListPushFront(Dlist, 78);DListPushFront(Dlist, 666);		//头插DListPrint(Dlist);				//打印DListPopFront(Dlist);			//头删DListPrint(Dlist);				//打印return 0;
}

运行结果:
运行结果

3. 误区纠正

  1. 增删查改的操作对象的主体不是头结点,头的增删动的是 head->next
  2. 头结点只是起辅助作用,判断带头链表是否为空,要从头结点->next开始判断。
  3. 双向链表不能完全替代单链表,单链表相对于双向链表来说 内存使用少 ,比双向链表少一个指针。

4. 正确使用链表

  • 理解 prevnext 的指向关系。
  • 根据实际情况判断链表是否需要 头结点循环双向

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

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

相关文章

Java后端技术博客汇总文档

文章目录 前言Java后端汇总链接Java基础知识点数据结构算法&#xff08;Java实现&#xff09;算法知识点合集算法刷题算法竞赛AcWing课程蓝桥杯AB组辅导课合集&#xff08;更新中…&#xff09; 源码分析redission 数据库SQL ServerMySQLRedis -Canal JUC并发编程JVMNetty日志框…

QT 菜单栏设计使用方法

目录 常用设置函数 多个QAction的单选设置 ​​​​​​​菜单相关类 ​​​​​​​ 系统菜单的生成和响应 使用代码添加系统菜单 使用UI设计器设计系统菜单 使用Qt设计及界面时&#xff0c;常用的两种方式添加菜单&#xff0c;第一使用UI界面添加&#xff0c;第二种 在…

AIGC领域AI艺术,打造个性化艺术作品

AIGC领域AI艺术,打造个性化艺术作品 关键词:AIGC、AI艺术、生成对抗网络、个性化创作、深度学习、艺术风格迁移、创意计算 摘要:本文深入探讨了AIGC(人工智能生成内容)在艺术创作领域的应用,重点分析了如何利用AI技术打造个性化艺术作品。文章从技术原理出发,详细解析了生…

基于Flask+Jinja2的快捷教务系统(后端链接到新版正方教务系统)

快捷教务系统&#xff08;Easy Educational Administration Management System, EasyEAMS&#xff09; 项目简介 EasyEAMS 是一个基于 Flask Jinja2 的现代化教务系统 Web 应用。学生可通过网页端登录&#xff0c;在线查询个人信息、成绩、课表、学业生涯、通知、选课等。系…

EDM自动化与出海独立开发实用教程

随着互联网全球化发展&#xff0c;越来越多的独立开发者&#xff08;Indie Developer&#xff09;选择将自己的产品推向海外市场。如何高效地获客、激活用户、提升转化率&#xff0c;成为出海过程中必须解决的问题。EDM&#xff08;电子邮件营销&#xff09;自动化&#xff0c;…

「日拱一码」017 深度学习常用库——TensorFlow

目录 基础操作 张量操作&#xff1a; tf.constant 用于创建常量张量 tf.Variable 用于创建可训练的变量张量 tf.reshape 可改变张量的形状 tf.concat 可将多个张量沿指定维度拼接 tf.split 则可将张量沿指定维度分割 数学运算&#xff1a; tf.add 张量的加运算 tf.su…

ARM DStream仿真器脚本常用命令

以下是ARM DStream仿真器脚本中常用的命令及其功能分类&#xff0c;结合调试流程和典型应用场景整理&#xff1a; ⚙️ 一、连接与初始化命令 connect 建立与目标设备的连接&#xff0c;需指定接口类型&#xff08;如JTAG/SWD&#xff09;和处理器核心。 示例&#xff1a;conne…

vscode 调试unity

lanch.json { “version”: “0.2.0”, “configurations”: [ { “name”: “Attach to Unity”, “type”: “vstuc”, “request”: “attach” } ] }

金融IT入门知识点

银行金融IT核心知识点全解析&#xff1a;架构、技术与实践 一、金融IT的战略地位与行业特性 金融IT作为银行业务的核心支撑体系&#xff0c;其发展水平直接决定了银行服务的效率、安全性与创新能力。截至 2025年&#xff0c;中国银行业线上化业务占比已达97%&#xff0c;手机银…

C++——手撕智能指针、单例模式、线程池、String

智能指针今天我们来学习一下C中的智能指针&#xff0c;如果有人不知道C中的智能指针的概念的话&#xff1a;C智能指针是一种基于RAII&#xff08;Resource Acquisition Is Initialization&#xff0c;资源获取即初始化&#xff09;机制的高级内存管理工具&#xff0c;用于自动化…

Mybatis----留言板

基础项目&#xff1a;留言板 截止到目前为止&#xff0c;我们已经学习了 Spring&#xff08;只学习了DI&#xff09;、Spring MVC、SpringBoot、Mybatis 这些知识了&#xff0c;已经满足了做简单项目的基本要求了&#xff0c;所以接下来我们就从0到1实现表白墙项目。 需求分析…

Web-API-day3 DOM事件进阶

一、 事件流 1.事件冒泡 const fa document.querySelector(.father)const son document.querySelector(.son)document.addEventListener(click, function () {alert(我是爷爷)})fa.addEventListener(click, function () {alert(我是爸爸)})son.addEventListener(click, fun…

小波增强型KAN网络 + SHAP可解释性分析(Pytorch实现)

效果一览一、传统KAN网络的痛点与突破 1. 传统KAN的局限性 传统Kolmogorov-Arnold网络&#xff08;KAN&#xff09;虽在理论上有可靠的多变量函数逼近能力&#xff0c;但存在显著瓶颈&#xff1a; 计算效率低&#xff1a;训练速度慢于MLP&#xff0c;资源消耗大&#xff0c;尤其…

tomcat部署多个端口以及制定路径部署-vue3

vue3项目tomcat部署记录 使用hash路由 字符串拼接的图片地址可以使用import.meta.env.BASE_URL 默认8080 如果部署地址为8080/xc 则设置 vite.config.js中设置base为’/xc/’ outDir设置为xc 打包产物直接拖到webapps目录下 如果另开一个端口 如8081 设置根目录访问 conf/ser…

LeetCode三数之和-js题解

给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复的三元组。 示例 1&…

Flink SQLServer CDC 环境配置与验证

一、SQL Server 数据库核心配置 1. 启用 CDC 功能&#xff08;Change Data Capture&#xff09; SQL Server CDC 依赖数据库级别的 CDC 功能及表级别的捕获配置&#xff0c;需按以下步骤启用&#xff1a; 启用数据库 CDC -- 以管理员身份连接数据库 USE master; GO-- 检查数…

软考(软件设计师)存储管理—设备管理,磁盘调度

I/O软件的核心目标是管理硬件差异、提供统一接口、实现高效可靠的数据传输。 核心目标&#xff1a; 设备无关性&#xff1a; 应用程序无需关心具体硬件细节。错误处理&#xff1a; 处理硬件错误和传输异常。同步/异步传输&#xff1a; 支持阻塞&#xff08;等待完成&#xff09…

[C语言] C语言数学函数库概览

C语言数学函数库概览 文章目录 C语言数学函数库概览一、概述二、基本数学函数详解1. 平方根函数 sqrt(x)2. 幂函数 pow(x, y)3. 绝对值函数 fabs(x)4. 向上取整函数 ceil(x)5. 向下取整函数 floor(x) 三、三角函数与双曲函数详解1. 正弦函数 double sin(double x)2. 余弦函数 d…

【简单三步】Stable diffusion Webai本地部署无法加载模型并报openai/clip-vit-large-patch14错误的解决方法

问题描述 Stable diffusion Webai本地部署成功后&#xff0c;手动加载本地模型checkpoint时&#xff0c;始终无法加载进去&#xff0c;确定模型存放位置无误&#xff08;位于models\Stable-diffusion&#xff09;查看cmd窗口时&#xff0c;发现一个报错提示&#xff1a;Can’t …

Java 命令行参数详解:系统属性、JVM 选项与应用配置

Java 命令行参数详解&#xff1a;系统属性、JVM 选项与应用配置 在 Java 应用启动命令中&#xff0c;如&#xff1a; java -jar -Dserver.port8088 xdr-demo-1.0-SNAPSHOT-assembly.jar &-Dserver.port8088是一个 系统属性&#xff08;System Property&#xff09; 设置。…