数据结构(双向链表——c语言实现)

双向链表相比于单向链表的优势:

1. 双向遍历的灵活性

  • 双向链表:由于每个节点都包含指向前一个节点和下一个节点的指针,因此可以从头节点遍历到尾节点,也可以从尾节点遍历到头节点。这种双向遍历的灵活性使得在某些算法和操作中,双向链表能够提供更高效的解决方案。

  • 单向链表:只能从头节点开始顺序遍历到尾节点,无法直接访问前驱节点,因此在某些需要双向遍历的场合下,单向链表显得不够灵活。

2. 插入和删除操作的便捷性

  • 双向链表:在双向链表中插入或删除一个节点时,只需要修改相邻节点的前后指针,而不需要遍历整个链表来查找前驱或后继节点。这大大提高了插入和删除操作的效率。

  • 单向链表:在插入或删除节点时,通常需要遍历链表以找到插入或删除位置的前一个节点,这增加了操作的复杂性。

3. 适用于复杂操作

  • 双向链表:由于可以方便地访问前驱和后继节点,双向链表在实现一些复杂操作时(如反转链表、合并链表等)变得更加简单和直观。

  • 单向链表:由于只能单向遍历,因此在实现这些复杂操作时可能需要更多的辅助变量和步骤。

4. 内存开销的考虑

  • 双向链表:每个节点需要额外存储一个指向前驱节点的指针,因此相对于单向链表,双向链表占用更多的内存空间。然而,这种额外的内存开销通常是可以接受的,特别是在需要频繁访问前驱节点的场合下。

  • 单向链表:由于每个节点只存储一个指向后继节点的指针,因此内存开销相对较小。但在需要访问前驱节点的场合下,可能需要通过额外的操作或数据结构来实现。

双向链表:

双向链表跟单向链表最大的区别就在于它的节点里面多了一个指向前一个节点的指针,我们还是约定头结点不存数据,同样还是多文件封装的形式。

#ifndef _DOUBLELINK_H
#define _DOUBLELINK_H#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>  //fabs头文件#define OUT(A) {printf("%.2f ",A);}
typedef float Type;
#define PRE 0.000001 //精度typedef struct node{Type data;           //存值struct node *front;  //前指针struct node *rear;   //后指针
}list;
//头节点不存数据//创建
list *create_link();
//判空
int empty_link(list *l);
//求长度
int length_link(list *l);
//遍历
void traverse_link(list *l);
//首插
void head_insert_link(list *l,Type data);
//尾插
void tail_insert_link(list *l,Type data);
//查找
list *search_link(list *l,Type data);
//修改
void update_link(list *l,Type oldData,Type newData);
//删除
void delete_link(list *l,Type data);
//初始化
void init_link(list *l);
//回收
void recycle_link(list **l);#endif

       双向链表的结构体里有三个成员,存储数据的变量data,指向上一个节点的指针front和指向下一个节点的指针rear;在这里浮点型数据是有精度的,它只是无限接近于我们输入的值,并不是完全相同,例如输入的是1.2,但是实际存储的数据为1.20000005;因此我将PRE宏定义为精度,这里是精确到6位小数,便于我们后续的查找中使用。 

#include "doublelink.h"//创建
list *create_link()
{list *l = (list *)malloc(sizeof(list));if(NULL == l){perror("create malloc");return NULL;}l->front = NULL;l->rear = NULL;return l;
}
//判空
int empty_link(list *l)
{if(l == NULL){puts("list is NULL");return -1;}return NULL == l->rear?0:1;
}
//求长度
int length_link(list *l)
{
#if 0if(l->rear == NULL) //递归求长度return 0;return 1+length_link(l->rear);
#elseif(l == NULL){puts("list is NULL");return -1;}int n = 0;while(l->rear){n++;l = l->rear;}return n;
#endif
}
//遍历
void traverse_link(list *l)
{if(l == NULL){puts("list is NULL");return;}while(l->rear){l = l->rear;OUT(l->data);}puts("");
}
//首插
void head_insert_link(list *l,Type data)
{if(l == NULL){puts("list is NULL");return;}list *p = (list *)malloc(sizeof(list));if(NULL == p){perror("create malloc");return;}p->data = data;p->front = l;p->rear = l->rear;l->rear = p;if(NULL != p->rear)p->rear->front = p;
}
//尾插
void tail_insert_link(list *l,Type data)
{if(l == NULL){puts("list is NULL");return;}list *p = (list *)malloc(sizeof(list));if(NULL == l){perror("create malloc");return;}while(l->rear){l = l->rear;}p->data = data;l->rear = p;p->front = l;p->rear = NULL;
}
//查找
list *search_link(list *l,Type data)
{if(l == NULL){puts("list is NULL");return NULL;}while(l->rear){l = l->rear;float t = l->data - data;if(fabs(t)<PRE)return l;}return NULL;
}
//修改
void update_link(list *l,Type oldData,Type newData)
{
#if 1if(l == NULL){puts("list is NULL");return;}while(l->rear){l = l->rear;if(oldData == l->data){l->data = newData;}}
#elselist *p = search_link(l,oldData);p->data = newData;
#endif
}
//删除
void delete_link(list *l,Type data)
{
#if 1if(l == NULL){puts("list is NULL");return;}while(l->rear){if(data == l->rear->data){list *p = l->rear;if(NULL != l->rear->rear)l->rear->rear->front = l;l->rear = l->rear->rear;free(p);}elsel = l->rear;}
#elselist *p;while(p=search_link(l,data)){p->front->rear = p->rear;if(p->rear)p->rear->front = p->front;free(p);}
#endif
}
//初始化
void init_link(list *l)
{if(l == NULL){puts("list is NULL");return;}
#if 1while(l->rear){list *p = l->rear;if(NULL != l->rear->rear)l->rear->rear->front = l;l->rear = l->rear->rear;free(p);}
#elsewhile(l->rear){list *p = l->rear;//这里不需要去管要删除节点后一个的前指针,因为所有的都要删除l->rear = p->rear;free(p);}
#endif
}
//回收
void recycle_link(list **l)
{if(l == NULL){puts("list is NULL");return;}init_link(*l);free(*l);*l = NULL;
}

创建:list *create_link()

       创建函数的返回值还是节点的地址,创建头节点之后要让它的两个指针都指向NULL,因为这时只有一个头结点,没有其他节点。

判空:int empty_link(list *l)

       双向链表的判空跟单向链表是一样的,只需要判断头节点的下一个节点是否为空(NULL)即可,空函数返回0,非空函数返回1。

求长度:int length_link(list *l)

       求长度只需要在遍历的时候使用一个变量来统计节点的数量即可;在这里我使用了两种方法来求长度,一种就是遍历计数;另外一种是使用递归函数来求长度,每次调用自己的时候都+1,就变成了有几个节点就是几个1相加  0,这样也可以求长度,而且代码更加简洁。 

遍历:void traverse_link(list *l)

       使用循环来遍历,让L每次往后移动一个节点,直到最后一个节点为止,每次循环都将节点的数据输出,就实现了遍历。

首插(头插):void head_insert_link(list *l,Type data)

       头部插入我们需要做的事情有给节点data赋值,给节点指针指向,断链;首先创建节点,创建完成之后给data赋值;然后让新节点的front指向头节点,让rear指向头节点的下一个节点,让头节点的rear指向新节点;最后就是让新节点的下一个节点的front指向新节点,但是在这里需要注意一个问题,如果原链表是空链表,那么就不能对新节点的下一个进行p->rear->front = p;操作,因为这时它为空,所以在这里就需要做一个判断,如果新节点的下一个节点不为空,那么才让它的front指向新节点,否则就不需要执行这一步操作。

尾插:void tail_insert_link(list *l,Type data)

       尾部插入相对头部插入就要简单一些,首先是给新节点的data赋值,然后循环遍历找到最后一个节点,让最后一个节点的rear指向新节点,让新节点的front指向最后这个节点,最后让新节点的rear指向NULL即可。

查找:list *search_link(list *l,Type data)

       查找需要返回节点的地址,因此函数为list *类型,同样使用循环的方式,但是在这里不建议使用data == l->data的方式来寻找所需要的节点,因为浮点型数据存储并不是完全与输入的数据相等,因此我们将查找的data与链表节点中的data做一个差,根据这个差值来判断是否是我们寻找的数据,这里就用到了我一开始定义的精度,差值在精度范围内就说明这个数据是我们要寻找的数据;在这里因为差值可能为负数,因此我使用fabs这个函数来将差值转化为绝对值,只要绝对值小于我们的精度,那就返回这个节点的地址,要是循环遍历结束都没有找到这个数据,那就返回一个NULL。

      在这里我使用的是fabs函数而不是abs函数是因为abs在将浮点型数据取绝对值的时候只能将它的整数部分取绝对值,而不能将整个浮点型数据取绝对值,但是fabs就能将整个浮点型数据取绝对值,这一点需要宝子们注意。

abs函数和fabs函数关键的区别

  1. 数据类型

    • abs函数用于计算整数的绝对值。它的参数和返回值都是整数类型(int)。

    • fabs函数用于计算浮点数的绝对值。它的参数是浮点类型(double),返回值也是double类型。

  2. 头文件

    • 要使用abs函数,你需要包含stdlib.h头文件。

    • 要使用fabs函数,你需要包含math.h头文件。

  3. 用途

    • 当你的数据是整数时,使用abs函数。

    • 当你的数据是浮点数时,使用fabs函数。

修改:void update_link(list *l,Type oldData,Type newData)

       数据的修改就很简单啦,通过循环遍历找到要修改的数据,将它直接修改为要更改的值即可,在这里可以偷懒调用之前的查找函数来帮助我们找到要修改的节点,然后直接修改内容就行,这样就不用再写一遍遍历啦。

删除:void delete_link(list *l,Type data)

       删除节点要做的有两件,释放节点和断链;断链也就是更改删除节点前后节点的指针指向,在这里把要删除的节点叫做目标节点;既然是删除节点,那就需要使用一个指针来指向这个节点,然后让目标节点的前一个节点的rear指向目标节点的下一个节点,在这里同样需要判断目标节点的下一个节点是否为空,如果非空就需要让该节点的front指向目标节点的前一个节点。

初始化:void init_link(list *l)

       双向链表的初始化和单向链表类似,只需要将每一个节点空间都释放掉即可,同样是通过循环遍历的方式;同样的也可以调用之前的删除函数,但是在这里我们可以不需要去管节点的前指针front,因为我们每一个节点都需要删除,将空间释放掉之后front也就不存在了。

回收:void recycle_link(list **l)

       回收函数的传参传的同样是链表头节点的地址,首次是初始化,然后再将头节点也释放,因此可以调用之前的初始化函数,然后再让这个指针指向NULL即可。

测试(主函数):

#include "doublelink.h"
int main(int agrc,char *agrv[])
{list *p = create_link();puts("尾插");tail_insert_link(p,1.25);tail_insert_link(p,1.2);tail_insert_link(p,1.1);traverse_link(p);puts("头插");head_insert_link(p,1.5);head_insert_link(p,1.10);head_insert_link(p,1.4);traverse_link(p);printf("length=%d\n",length_link(p));puts("将1.10更改为1.6");update_link(p,1.10,1.6);traverse_link(p);puts("删除1.2");delete_link(p,1.2);traverse_link(p);puts("查找1.50,利用找到的节点将它修改为2.00");list *q = search_link(p,1.50);q->data = 2.00;traverse_link(p);puts("初始化");init_link(p);printf("length=%d\n",length_link(p));puts("回收");recycle_link(&p);printf("p=%p\n",p);return 0;
}

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

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

相关文章

论文分享 | FuzzLLM:一种用于发现大语言模型中越狱漏洞的通用模糊测试框架

大语言模型是当前人工智能领域的前沿研究方向&#xff0c;在安全性方面大语言模型存在一些挑战和问题。分享一篇发表于2024年ICASSP会议的论文FuzzLLM&#xff0c;它设计了一种模糊测试框架&#xff0c;利用模型的能力去测试模型对越狱攻击的防护水平。 论文摘要 大语言模型中…

ES分词环境实战

文章目录 安装下载1.1 下载镜像1.2 单节点启动 防火墙设置异常处理【1】iptable链路中断 参考文档 参加完2024年11月软考&#xff0c;对ES的分词进行考查&#xff0c;前期有【 Docker 环境下安装部署 Elasticsearch 和 kibana】和【 Docker 环境下为 Elasticsearch 安装IK 分…

在 CentOS 系统上直接安装 MongoDB 4.0.25

文章目录 步骤 1&#xff1a;配置 MongoDB 官方源步骤 2&#xff1a;安装 MongoDB步骤 3&#xff1a;启动 MongoDB 服务步骤 4&#xff1a;验证安装步骤 5&#xff1a;可选配置注意事项 以下是在 CentOS 系统上直接安装 MongoDB 4.0.25 的详细步骤&#xff1a; 步骤 1&#x…

基于Vue+SpringBoot的求职招聘平台

平台概述 本平台是一个高效、便捷的人才与职位匹配系统&#xff0c;旨在为求职者与招聘者提供一站式服务。平台内设三大核心角色&#xff1a;求职者、招聘者以及超级管理员&#xff0c;每个角色拥有独特的功能模块&#xff0c;确保用户能够轻松完成从信息获取到最终录用的整个…

谈谈Spring的常见基础概念

文章是对Spring一些基础的底层概念进行分析&#xff0c;后续再遇到这些问题的时候&#xff0c;可以采用这些步骤进行详细解释。 一.谈谈SpringIOC的理解&#xff0c;原理与实现? 总&#xff1a; 1.控制反转&#xff1a; (1)原来的对象是由使用者来进行控制&#xff0c;有了S…

NAT网络地址转换——Easy IP

NAT网络地址转换 Tip&#xff1a; EasylP没有地址池的概念,使用接口地址作为NAT转换的公有地址。EasylP适用于不具备固定公网IP地址的场景:如通过DHCP, PPPOE拨号获取地址的私有网络出口,可以直接使用获取到的动态地址进行转换。 本次实验模拟nat协议配置 AR1配置如下&…

基于xr-frame实现微信小程序的手部、手势识别3D模型叠加和石头剪刀布游戏功能

前言 xr-frame是一套小程序官方提供的XR/3D应用解决方案&#xff0c;基于混合方案实现&#xff0c;性能逼近原生、效果好、易用、强扩展、渐进式、遵循小程序开发标准。xr-frame在基础库v2.32.0开始基本稳定&#xff0c;发布为正式版&#xff0c;但仍有一些功能还在开发&#…

【WRF-Urban】URBPARM_LCZ.TBL 查找表解释及内容

【WRF-Urban】URBPARM_LCZ.TBL 查找表解释及内容 URBPARM_LCZ.TBL 文件的作用URBPARM_LCZ.TBL 文件中的参数URBPARM_LCZ.TBL 的使用URBPARM_LCZ.TBL 文件内容如何调整或扩展 URBPARM_LCZ.TBL参考URBPARM_LCZ.TBL 文件是 WRF(天气研究与预报模型) 中用于处理 局地气候区(Loca…

nacos开启鉴权与配置加密

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、Nacos漏洞复现 1.1.查看配置 1.2.查看用户列表 1.3.注册新用户 二、Nacos开启鉴权 三、变更配置与信息加密 1.变更配置 2.信息加密 四、增强安全性 五、常见问…

AI Large Language Model

AI 的 Large Language model LLM , 大语言模型&#xff1a; 是AI的模型&#xff0c;专门设计用来处理自然语言相关任务。它们通过深度学习和庞大的训练数据集&#xff0c;在理解和生成自然语言文本方面表现出色。常见的 LLM 包括 OpenAI 的 GPT 系列、Google 的 PaLM 和 Meta…

前端三剑客(二):CSS

目录 1. CSS 基础 1.1 什么是 CSS 1.2 语法格式 1.3 引入方式 1.3.1 行内样式 1.3.2 内部样式 1.3.3 外部样式 1.4 CSS 编码规范 2. 选择器 2.1 标签选择器 2.2 id 选择器 2.3 class 选择器(类选择器) 2.4 复合选择器 2.5 通配符选择器 3. 常用 CSS 样式 3.1 c…

华为Ensp模拟器配置OSPF路由协议

目录 简介 实验步骤 Pc配置 路由器配置 OSPF配置 交换机配置 简介 开放式最短路径优先 (OSPF) 协议深度解析 简介 开放式最短路径优先&#xff08;Open Shortest Path First, OSPF&#xff09;是一种内部网关协议&#xff08;IGP&#xff09;&#xff0c;用于在自治系统…

【C++】绘制内存管理的地图

生活是属于每个人自己的感受&#xff0c;不属于任何人的看法。 前言 这是我自己学习C的第二篇博客总结。后期我会继续把C学习笔记开源至博客上。 上一期笔记是关于C的类与对象础知识&#xff0c;没看的同学可以过去看看&#xff1a; 【C】面向对象编程的艺术之旅-CSDN博客https…

基于YOLOv8深度学习的医学影像骨折检测诊断系统研究与实现(PyQt5界面+数据集+训练代码)

本论文深入研究并实现了一种基于YOLOV8深度学习模型的医学影像骨折检测与诊断系统&#xff0c;旨在为医学影像中的骨折检测提供高效且准确的自动化解决方案。随着医疗影像技术的快速发展&#xff0c;临床医生需要从大量复杂的医学图像中精确、快速地识别病灶区域&#xff0c;特…

【vulhub】nginx解析漏洞(nginx_parsing_vulnerability)

1. nginx解析漏洞原理 fastcgi 在处理’.php’文件时发现文件并不存在,这时 php.ini 配置文件中cgi.fix_pathinfo1 发挥作用,这项配置用于修复路径,如果当前路径不存在则采用上层路径 (1)由于 nginx.conf的配置导致 nginx把以’.php”结尾的文件交给 fastcgi 处理,为此可以构造…

如何通过统一权限管理打破异构系统的安全屏障

企业在运营过程中面临着众多异构系统的整合挑战&#xff0c;这些异构系统由于其不同的技术架构、数据格式和安全机制等&#xff0c;给信息管理带来了诸多挑战。其中&#xff0c;“信息孤岛”问题尤为突出&#xff0c;而异构环境下的统一授权管理系统则成为解决这一问题的关键。…

阿里云IIS虚拟主机部署ssl证书

宝塔配置SSL证书用起来是很方便的&#xff0c;只需要在站点里就可以配置好&#xff0c;但是云虚拟主机在管理的时候是没有这个权限的&#xff0c;只提供了简单的域名管理等信息。 此处记录下阿里云&#xff08;原万网&#xff09;的IIS虚拟主机如何配置部署SSL证书。 进入虚拟…

Linux系列-僵尸状态

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 进程退出 进程退出之后&#xff0c;代码就不会执行了&#xff0c;而是由PCB维护起来&#xff0c;我们可以通过PCB来查看退出信息。 进程退出时首先可以立即释放的就是进程对应…

DBeaver中PostgreSQL数据库显示不全的解决方法

本文介绍在DBeaver中&#xff0c;连接PostgreSQL后&#xff0c;数据库显示不全的解决方法。 最近&#xff0c;在DBeaver中连接了本地的PostgreSQL数据库。但是连接后打开这个数据库时发现&#xff0c;其所显示的Databases不全。如下图所示&#xff0c;Databases只显示了一个pos…

pycharm中配置pyqt5

pycharm中配置pyqt5 Python提供了多种图形界面库&#xff0c;包括但不限于Tkinter、PyQt、wxPython、Kivy等。Tkinter由于其轻量级和跨平台特性&#xff0c;通常作为入门首选。PyQt和wxPython则提供了更多的控件和更强大的功能&#xff0c;适合于需要复杂用户界面的应用程序。…