链表最终章——双向链表及其应用

———————————本文旨在交流探讨计算机知识,欢迎交流指正————————————

        上一章,我们介绍了链表的循环扩展,但是,单向链表毕竟是单向查询的,就算是经过循环来查找,终究是效率偏低,下面,我们来介绍一种工程项目中运用更加广泛的一种链表结构:双向链表——:

        根据此结构,我们可以发现,虽然代码复杂度增加了,但是由于此结构是双向的,操作更加便捷,下面,我将演示各模块内容:

首先,是结构的构建:

typedef int Element;
typedef struct _node {Element val;struct _node *next;struct _node *prev;
} DNode, DList;/* 使用一个带头节点的双向循环链表,头节点让用户来管理,提供初始化接口 */
void initDList(DList *header);
void releaseDList(DList *header);// 实现头插、尾插
void insertDListHeader(DList *header, Element val);
void insertDListRear(DList *header, Element val);
// 显示链表
void showDList(const DList *header);
// 删除一个元素
void delDList(DList *header, Element e);

注:此为.h头文件里的内容,若先用头文件封装再放在.c文件里面使用,最后在main.c文件里面测试,很好的保证了产品内容的安全性与代码便捷性。

1、这一次,为了让代码更加简洁规范,我们使用static的模式构建加入和删除两大基本模块:
 

//next是已知的待插入位置节点的下一个节点;
//prev是已知的待插入节点的上一个节点(前置节点);//插入
static void addDNode(DNode *new_node, DNode *prev, DNode *next) {next->prev = new_node;//待插入节点下一个节点的上一个节点是新节点;new_node->next = next;//新节点的下一个是上一个节点;new_node->prev = prev;新节点上一个是前置节点prev->next = new_node;前置节点下一个是新节点
}//删除
static void delDNode(DNode *prev, DNode *next) {next->prev = prev;//某节点下一个节点的前一个是某节点上一个节点;prev->next = next;//某节点上一个节点的下一个是某节点下一个节点;
}

2、然后,我们再在此基础上完成增删改查的基本操作:

1、初始化:

void initDList(DList* header) {header->val = 0;//初始化头结点的数值域为0header->next = header->prev = header;//头结点的前置节点和后置节点都初始化指向头结点
}

2、增添操作:

void insertDListHeader(DList* header, Element val) {DNode *new_node = malloc(sizeof(DNode));//malloc申请空间建立一个新节点new_node->val = val;//新节点赋值;addDNode(new_node, header, header->next);//函数操作++header->val;
}
//头插法;void insertDListRear(DList* header, Element val) {DNode *new_node = malloc(sizeof(DNode));new_node->val = val;addDNode(new_node, header->prev, header);++header->val;
}
//尾插法;

3、删除操作

void delDList(DList* header, Element e) {// 1. 找到这个元素,就可以删除,不需要再找到前置节点DNode *pos = header->next;while (pos != header && pos->val != e) {pos = pos->next;}// 2. 找到没有?if (pos != header) {	// 找到delDNode(pos->prev, pos->next);pos->next = pos->prev = NULL;free(pos);--header->val;} else {printf("Not find %d element!\n", e);}

4、展示函数(若对工程有疑问和对const有疑问可以看本专栏的第一篇内容)

void showDList(const DList* header) {DNode *pos = header->next;printf("show: ");while (pos != header) {printf("\t%d", pos->val);pos = pos->next;}printf("\n");
}
//简单的循环打印,不过多赘述;

5、最后,是销毁链表的函数:

void releaseDList(DList* header) {DNode *pos = header->next;DNode *tmp = NULL;while (pos != header) {tmp = pos;delDNode(pos->prev, pos->next);pos = pos->next;free(tmp);--header->val;}
}
//逐个销毁

最后附上各文件代码:

1、doubleList.h:

#ifndef DOUBLE_LIST_H
#define DOUBLE_LIST_Htypedef int Element;
typedef struct _node {Element val;struct _node *next;struct _node *prev;
} DNode, DList;/* 使用一个带头节点的双向循环链表,头节点让用户来管理,提供初始化接口 */
void initDList(DList *header);
void releaseDList(DList *header);// 实现头插、尾插
void insertDListHeader(DList *header, Element val);
void insertDListRear(DList *header, Element val);
// 显示链表
void showDList(const DList *header);
// 删除一个元素
void delDList(DList *header, Element e);#endif //DOUBLE_LIST_H

2、doubleList.c

#include "doubleList.h"//doubleList.h文件封装的函数名
#include <stdlib.h>
#include <stdio.h>static void addDNode(DNode *new_node, DNode *prev, DNode *next) {next->prev = new_node;new_node->next = next;new_node->prev = prev;prev->next = new_node;
}static void delDNode(DNode *prev, DNode *next) {next->prev = prev;prev->next = next;
}void initDList(DList* header) {header->val = 0;header->next = header->prev = header;
}void releaseDList(DList* header) {DNode *pos = header->next;DNode *tmp = NULL;while (pos != header) {tmp = pos;delDNode(pos->prev, pos->next);pos = pos->next;free(tmp);--header->val;}
}void insertDListHeader(DList* header, Element val) {DNode *new_node = malloc(sizeof(DNode));new_node->val = val;addDNode(new_node, header, header->next);++header->val;
}void insertDListRear(DList* header, Element val) {DNode *new_node = malloc(sizeof(DNode));new_node->val = val;addDNode(new_node, header->prev, header);++header->val;
}void showDList(const DList* header) {DNode *pos = header->next;printf("show: ");while (pos != header) {printf("\t%d", pos->val);pos = pos->next;}printf("\n");
}void delDList(DList* header, Element e) {// 1. 找到这个元素,就可以删除,不需要再找到前置节点DNode *pos = header->next;while (pos != header && pos->val != e) {pos = pos->next;}// 2. 找到没有?if (pos != header) {	// 找到delDNode(pos->prev, pos->next);pos->next = pos->prev = NULL;free(pos);--header->val;} else {printf("Not find %d element!\n", e);}
}

3、main.c

#include <stdio.h>
#include "doubleList.h"DList stu_table;int main() {initDList(&stu_table);for (int i = 0; i < 5; ++i) {// insertDListHeader(&stu_table, i + 100);insertDListRear(&stu_table, i + 100);}insertDListHeader(&stu_table, 60);insertDListHeader(&stu_table, 80);showDList(&stu_table);printf("====================\n");delDList(&stu_table, 102);showDList(&stu_table);releaseDList(&stu_table);printf("num: %d\n", stu_table.val);showDList(&stu_table);return 0;
}

 ————————————希望能对你有所帮助!!!————————————————

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

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

相关文章

智能体的5个核心要素

文章目录 如何看待智能体智能体的发展阶段国内大模型厂家推出的智能体智能体的应用领域智能体架构智能体的核心要素1. ​​认知中枢&#xff08;大模型&#xff09;​​&#x1f9e0; 2. ​​记忆系统&#xff08;Memory&#xff09;​​&#x1f6e0;️ 3. ​​规划与决策&…

QUdpScoket 组播实现及其中的踩坑点记录

QUdpScoket 组播实现及其中的踩坑点记录 QUdpSocket要想组播需要打开MulticastTtlOption配置项&#xff0c;否则无法生效&#xff0c;亲身踩坑经历 m_socketnew QUdpSocket(this);m_socket->setSocketOption(QAbstractSocket::MulticastTtlOption,1);确定一个组播地址&…

250627-结合Guacamole与FRP访问CentOS-Stream-9及Windows10

A. FRP的配置 A.1 FRP在CentOS中的配置 frps.toml [common] bind_port 7000 bind_addr 0.0.0.0dashboard_port 7500 dashboard_user admin dashboard_pwd admin启动&#xff1a;./frps -c frps.toml frpc.toml [common] server_addr 123.456.789.98 server_port 700…

环保法规下的十六层线路板创新:猎板 PCB 如何实现无铅化与可持续制造

在全球环保法规趋严的背景下&#xff0c;十六层线路板作为高端电子设备的核心组件&#xff0c;正面临无铅化与可持续制造的双重挑战。猎板 PCB 凭借材料革新与工艺升级&#xff0c;构建了从焊料到基材、从生产到回收的全链路绿色体系&#xff0c;为行业树立了合规标杆。 一、无…

OpenLayers 拖动旋转和缩放

前言 在 OpenLayers 框架中已经封装了很多便利的交互控件&#xff0c;可以做到开箱即用&#xff0c;非常方便。像拖动缩放、绘制、选择等交互控件可以供开发者直接使用。本篇给大家介绍拖动旋转交互控件 1. 旋转控件简介 此控件通过按住shift键结合鼠标左键或右键进行使用。在…

element ui Cascader 级联选择器 处理未全选时去除父节点值,选中父节点时去除子节点值

目前我这边的需求时&#xff1a;当用户的选择&#xff0c;只保留最顶层的选中节点 如果选中了父节点&#xff0c;则移除其所有子孙节点以及它的祖先节点&#xff08;因为选中父节点代表选中整个分支&#xff0c;所以不需要再显示子节点&#xff1b;同时&#xff0c;如果存在祖…

uniapp实现远程图片下载到手机相册功能

在 UniApp 中实现点击下载图片到相册的功能&#xff0c;需要以下几个步骤&#xff1a; 1. 下载图片到本地&#xff08;uni.downloadFile&#xff09; 2. 将图片保存到相册&#xff08;uni.saveImageToPhotosAlbum&#xff09; 完整代码示例&#xff1a; <template>&l…

【世纪龙科技】吉利博瑞汽车车身诊断与校正仿真教学软件

在汽车产业蓬勃发展的当下&#xff0c;汽车车身诊断与校正技术人才的需求与日俱增。然而&#xff0c;职业院校在汽车车身教学实践中&#xff0c;却面临着学生实训机会稀缺、教学互动匮乏、过程评价缺失、学生技能提升缓慢等诸多难题。江苏世纪龙科技凭借其卓越的技术实力与行业…

极速二刷leetcode hot100

简单题 1.移动0 283. 移动零 - 力扣&#xff08;LeetCode&#xff09; 刚开始没看到非零子串的顺序不变&#xff1a; // if(nums.size() 1){// return;// }// //所有 0 移动到数组的末尾//同时保持非零元素的相对顺序。// int n nums.size();// int notZero n-1;////…

技术博客:如何用针孔相机模型理解图像

引言 大家好&#xff01;今天我们来聊聊一个非常有趣的话题——针孔相机模型。这个模型可以帮助我们理解相机是如何捕捉图像的。我们会用一些简单的数学公式来解释这个过程&#xff0c;不用担心&#xff0c;我会尽量让这些内容简单易懂。 什么是针孔相机模型&#xff1f; 针…

Nanonets-OCR:Qwen2.5VL-3B的微调模型 更强大的文档解析能力|附效果实测

一 Nanonets-OCR 简介 Nanonets-OCR不再满足于单纯提取文本&#xff0c;它能智能解析图像中的公式、表格、水印、签名、图表、复选框等复杂结构&#xff0c;并输出格式清晰的 Markdown。 核心功能 ● LaTeX 公式识别&#xff1a;自动将文中数学公式转为标准 LaTeX 格式 ●…

Git下载与使用完全指南:从安装到基础操作详解,附上git的学习网站(很直观)(可以模拟git的全过程)

一、Git简介与下载安装 1.1 Git是什么&#xff1f; Git是目前世界上最先进的分布式版本控制系统&#xff0c;由Linus Torvalds&#xff08;Linux之父&#xff09;开发。它可以高效地处理从小型到大型项目的版本管理&#xff0c;具有以下特点&#xff1a; 分布式架构&#xff…

论分布式设计

20250419-作 题目 分布式是指将一个系统或任务分解成多个子部分&#xff0c;并在多个计算机或服务器之间进行协同工作的方式。每个子部分都可以在不同的计算机节点上运行&#xff0c;彼此之间通过网络进行通信和协调。分布式技术在当今互联网应用中起着重要作用&#xff0c;例…

Vue样式绑定与条件渲染详

一、Vue样式绑定 在Vue中&#xff0c;我们可以通过多种方式动态地绑定样式&#xff0c;让界面根据数据状态变化而自动更新样式。 1. class样式绑定 (1) 字符串写法 适用场景&#xff1a;样式的类名不确定&#xff0c;需要动态指定 <template><div><!-- 绑定…

固态电池火热-美国固态电池企业QuantumScape宣布,产能规模化迈出了重要一步

美国固态电池企业QuantumScape宣布&#xff0c;其先进的Cobra隔膜工艺已成功集成到基线电池生产中&#xff0c;标志着公司生产能力规模化迈出了重要一步。 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 600478 科力远 业绩固态电池 | 1.科力远发布20…

Python 商务数据分析—— NumPy 学习笔记Ⅰ

一、NumPy 简介 1.1 NumPy 特性 高性能科学计算库&#xff1a;专为处理多维数组设计&#xff0c;底层用 C 语言实现&#xff0c;运算速度远超 Python 原生列表。 矢量运算&#xff1a;支持批量数据操作&#xff0c;避免显式循环&#xff0c;代码更简洁高效。 广播机制&…

中州养老:搭建环境(第二节)

目录 项目初始工程搭建: 不同项目需要的前后端环境也不同 前端项目搭建: 熟悉模块的方式 代码阅读 如何开发一个接口 Swagger(接口文档) Api注解的说明 ​​​​​​​项目初始工程搭建: 公司项目分两种,新立项目(0-1)和已有项目开发(1-2) 熟悉项目结构,每个模块对应的…

[1-01-01].第78节:Java8新特性 - Lambda表达式

java基础语法大纲 一、Lambda 表达式 1.1.概述&#xff1a; 1.Lambda 是一个匿名函数&#xff0c;我们可以把 Lambda 表达式理解为是一段可以传递的代码&#xff08;将代码像数据一样进行传递&#xff09;2.使用Lambda 表达式可以写出更简洁、更灵活的代码。作为一种更紧凑的…

【2025.6.27 校内 NOI 模拟赛】总结(贪心, 容斥、组合计数, dsu on tree、 虚树)

文章目录 时间安排反思题解[六省联考 2017] 期末考试&#xff08;贪心&#xff0c; 枚举&#xff09;[JSOI2019] 神经网络&#xff08;容斥&#xff0c; 组合计数&#xff0c; 树背包&#xff09;[ZJOI2019] 语言&#xff08;dsu on tree&#xff0c; 虚树&#xff0c; 结论&am…

实际前端开发中,常用指令的封装

实际前端开发中&#xff0c;常用指令的封装 全局指令处理步骤main.ts指令目录结构src/directives/index.ts 一、输入框空格禁止指令1、指令文件clearSpace.ts2、指令使用 全局指令处理步骤 main.ts import { createApp } from "vue"; import App from "./App.…