数据结构-双向链表-003

1双向链表

1.1链表结点结构体定义

typedef struct student_data
{char name[32];char sex;int age;
}STU_DATA;
typedef struct double_link_node
{STU_DATA data;//数据域struct double_link_node *ppre;//指向上一个结点的指针struct double_link_node *pnext;//指向下一个结点的指针
}DOUB_NODE;

1.2链表头结点结构体定义

typedef struct double_link_head
{DOUB_NODE *phead;//指向首结点的指针int clen;//记录链表节点数量
}DOUB_HEAD;

1.3双向链表头结点创建

/*=============双向链表创建头结点(创建空表)=========*/
void *create_double_link_list_head_node(void)
{DOUB_HEAD *phead=NULL;/*创建头结点空间*/phead=malloc(sizeof(DOUB_HEAD));if(NULL==phead){perror("fail to malloc");return NULL;}/*头结点初始化*/phead->phead=NULL;phead->clen=0;return phead;
}

1.4双向链表结点创建

/*=============双向链表创建新结点==============*/
void* create_double_link_list_new_node(STU_DATA data)
{DOUB_NODE *pnode=NULL;/*创建新结点空间*/pnode=malloc(sizeof(DOUB_NODE));if(NULL==pnode){perror("fail to malloc");return NULL;}/*新结点初始化*/pnode->data = data;pnode->ppre=NULL;pnode->pnext=NULL;return pnode;
}

1.5链表非空判断

/*=============双向链表非空判断==============*/
int is_empty_link(DOUB_HEAD *list)
{return NULL==list->phead;
}

1.6双向链表结点插入

1.6.1头插法

/*=============双向链表头插法=================*/
int push_head_double_link_list(DOUB_HEAD *list, DOUB_NODE *pnode)
{if(NULL==list||NULL==pnode){return -1;}if(is_empty_link(list)){list->phead=pnode;}else{pnode->pnext=list->phead;//初始化新结点的后继为旧的首结点list->phead->ppre=pnode;//更新首结点的前驱结点为新结点list->phead=pnode;//更新头结点的指向为新结点(因为此时新结点为新的首结点)}list->clen++;return 0;
}

1.6.2尾插法

/*=============双向链表尾插法=================*/
int push_tail_double_link_list(DOUB_HEAD *list,DOUB_NODE *pnode)
{DOUB_NODE *ptmp=NULL;if(NULL==list||NULL==pnode){return -1;}if(is_empty_link(list)){list->phead=pnode;}else{/*定义一个遍历指针,初始化为指向首结点*/ptmp=list->phead;while(1){if(NULL==ptmp->pnext){break;}ptmp=ptmp->pnext;}pnode->pnext=ptmp->pnext;//初始化新结点的后继结点为NULL(原来尾结点的指针域)ptmp->pnext=pnode;//更新尾结点的后继结点为新结点pnode->ppre=ptmp;//初始化新结点的前继结点为尾结点}list->clen++;return 0;
}

1.7双向链表遍历

1.7.1低配版本遍历

/*=============双向链表遍历(原始版)===============*/
int double_list_for_each(DOUB_HEAD *list)
{DOUB_NODE *ptmp=NULL;/*判断空表*/if(is_empty_link(list)){return 0;}/*初始化遍历指针为指向首结点*/ptmp=list->phead;while(1){if(NULL==ptmp)//如果是空表,也满足,如果遍历到链表末尾,也满足条件{break;}else{printf("%-10s\t%-10c\t%-10d\n",ptmp->data.name,ptmp->data.sex,ptmp->data.age);ptmp=ptmp->pnext;}}return 0;
}

1.7.2指定遍历方法的遍历

/*=============双向链表遍历(改进版)===============*/
int double_list_for_each(DOUB_HEAD *list,int dir,void (*pfun)(DOUB_NODE *))
{DOUB_NODE *ptmp=NULL;/*判断空表*/if(is_empty_link(list)){return 0;}/*初始化遍历指针为指向首结点*/ptmp=list->phead;if(dir){while(1){if(NULL==ptmp)//如果是空表,也满足,如果遍历到链表末尾,也满足条件{break;}pfun(ptmp);ptmp=ptmp->pnext;}}else{/*获取尾结点*/while(1){if(NULL==ptmp->pnext){break;}ptmp=ptmp->pnext;}/*以尾结点为遍历起点,通过前继指针进行后序遍历*/while(1){if(NULL==ptmp){break;}pfun(ptmp);ptmp=ptmp->ppre;}}return 0;
}/*==========遍历方式==========*/
void show_data(DOUB_NODE *pnode)
{printf("%-10s\t%-10c\t%-10d\n",pnode->data.name,pnode->data.sex,pnode->data.age);
}

1.8双向链表结点删除

1.8.1头删法

/*==========双向链表头删法==========*/
int pop_head_double_link_list(DOUB_HEAD *list)
{DOUB_NODE *ptmp=NULL;if(is_empty_link(list)){return 0;}ptmp=list->phead;//初始化结点类型的中间指针变量为指向首结点list->phead=ptmp->pnext;//更新头结点的指向为首结点的后继结点ptmp->pnext->ppre=ptmp->ppre;//新的首结点的前继指针继承旧的首结点的前继指针free(ptmp);list->clen--;return 0;
}

1.8.2尾删法

/*==========双向链表尾删法==========*/
int pop_tail_double_link_list(DOUB_HEAD *list)
{DOUB_NODE *ptmp=NULL;if(is_empty_link(list)){return 0;}ptmp=list->phead;while(1){if(NULL==ptmp->pnext){break;}ptmp=ptmp->pnext;}if(ptmp->ppre!=NULL){ptmp->ppre->pnext=NULL;}else{list->phead=NULL;}free(ptmp);list->clen--;return 0;
}

1.9双向链表查找

/*==========查找结点==========*/
DOUB_NODE *search_double_link_list_node(DOUB_HEAD *list,int (*pfun)(DOUB_NODE *,void *),void *t)
{DOUB_NODE *ptmp=NULL;if(NULL==list||NULL==pfun||NULL==t){return NULL;}ptmp=list->phead;while(1){if(ptmp==NULL){break;}if(pfun(ptmp,t)){return ptmp;}ptmp=ptmp->pnext;}return NULL;
}/*==========查找方式==========*/
int search_by_name(DOUB_NODE *pnode,void *t)
{if(strcmp((char *)t,pnode->data.name)==0){return 1;}return 0;
}int search_by_sex(DOUB_NODE *pnode,void *t)
{if(*((char *)t)==pnode->data.sex){return 1;}return 0;
}int search_by_age(DOUB_NODE *pnode,void *t)
{if(*((int *)t)==pnode->data.age){return 1;}return 0;
}

1.10双向链表修改

通过调用双向链表的查找函数接口进行修改

1.11双向链表销毁

/*==========双向链表销毁==========*/
void destroy_double_link_list(DOUB_HEAD *list)
{while(1){if(is_empty_link(list)){break;}else{pop_head_double_link_list(list);
//            pop_tail_double_link_list(list);}free(list);}
}

1.12双向链表的其它操作

1.12.1双向链表逆序

/*==========双向链表逆序==========*/
void reverse_double_link_list(DOUB_HEAD *list)
{DOUB_NODE *ptmp=NULL;DOUB_NODE *pinsert=NULL;if(is_empty_link(list)){return ;}ptmp=list->phead;//初始化遍历指针为指向首结点list->phead=NULL;list->clen=0;//头结点与首结点断开,此时clen置0while(1){if(NULL==ptmp){break;}pinsert=ptmp;//获取当前结点为待插入结点ptmp=ptmp->pnext;pinsert->pnext=NULL;pinsert->ppre=NULL;push_head_double_link_list(list,pinsert);//用头插法将待插入结点插入}
}

1.12.2双向链表查找中间结点


1.12.3双向链表查找倒数第k个结点


1.12.4双向链表删除指定结点

1.12.4.1单结点删除
/*==========双向链表删除指定结点==========*/
int pop_appointed_node(DOUB_HEAD *list,int (*pfun)(DOUB_NODE *,void *),void *t)
{DOUB_NODE *pnode=NULL;if(NULL==list||NULL==pfun||NULL==t){return -1;}pnode=search_double_link_list_node(list,pfun,t);if(NULL==pnode){return -1;}if(NULL==pnode->ppre){pop_head_double_link_list(list);}else if(NULL==pnode->pnext){pop_tail_double_link_list(list);}else{pnode->ppre->pnext=pnode->pnext;pnode->pnext->ppre=pnode->ppre;free(pnode);list->clen--;}return 0;
}
1.12.4.2多结点删除
用于多个结点值相同的情况

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

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

相关文章

国内ip地址怎么改?详解修改ip地址的步骤

在网络通信中,IP地址是设备在网络上的标识,对于用户、服务器和网络安全都至关重要。然而,有时候在特定情况下,可能需要修改IP地址以满足不同需求或解决特定问题。虎观代理小二将深入研究中国国内IP地址修改的方法与影响&#xff0…

【算法】堆排序

1. 堆排序简介 堆排序(heapsort)是由 J. W. J. Williams 于 1964 年发明的。是一种基于比较的排序算法,和选择排序一样,堆排序将数据序列分为已排序区域和未排序区域两部分。通过从未排列区域中获取最大元素并将其插入已排序区域,迭代这个操作来缩小未排序区域。与选择排…

2024年华为OD机试真题-二叉树的广度优先遍历-Python-OD统一考试(C卷)

题目描述: 有一棵二叉树,每个节点由一个大写字母标识(最多26个节点)。现有两组字母,分别表示后序遍历(左孩子->右孩子->父节点)和中序遍历(左孩子->父节点->右孩子)的结果,请输出层次遍历的结果。 输入描述: 输入为两个字符串,分别是二叉树的后续遍历和…

LeetCode第三天(645. 错误的集合)

集合 s 包含从 1 到 n 的整数。不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,导致集合 丢失了一个数字 并且 有一个数字重复 。 给定一个数组 nums 代表了集合 S 发生错误后的结果。 请你找出重复出…

npm install jsencrypt爆错

报错: npm install jsencrypt npm ERR! code CERT_HAS_EXPIRED npm ERR! errno CERT_HAS_EXPIRED npm ERR! request to https://registry.npm.taobao.org/jsencrypt failed, reason: certificate has expired npm ERR! A complete log of this run can be found in: C:\Users…

C++ explicit隐式类型转换

单参数构造函数支持隐式类型的转换 什么意思? 简单来理解就是: 一个类对象的构造函数的参数只有一个,就可以直接进行赋值传参 例如构造函数的参数为int,且只有一个int 就可以直接将int类型的整型数据转换成类对象 也就是说从int类…

Unity构建详解(3)——SBP的依赖计算

【前置知识】 先要搞清楚Asset和Object的关系,可以简单理解为一个Asset对应多个Object。 unity自定义的Asset也要有一个存储的标准,其采用的是YAML,我们看到的所有Unity自定义的Asset格式,例如.prefab(预制体&#x…

研华工控机610L学习笔记2:visualstudio与第一个C#程序

今日继续学习工控机 C# 编程相关知识: 这篇结束后我将先进行一段时间的C#的学习研究,并写一些C#的笔记 后续再更新工控机编程设计相关 目录 1、安装visualstudio: 2、创建第一个C#程序: 3、寻找C#解决方案源文件: …

linux之zabbix自定义监控

zabbix基本配置见:写文章-CSDN创作中心https://mp.csdn.net/mp_blog/creation/editor/136783672 自定义监控规则 命令为who | wc -l 显示为2,主机一个,mobaxterm一个,思路是开启3个终端,让主机的zabbix服务自动检测1…

B端设计:如何让UI组件库成为助力,而不是阻力。

首发2023-09-24 15:42贝格前端工场 Hi,我是大千UI工场,网上的UI组件库琳琅满目,比如elementUI、antdesign、iview等等,甚至很多前端框架,也出了很多UI组件,如若依、Layui、bootstrap等等,作为U…

I/O多路复用:select/poll/epoll

最基本的 Socket 模型 要想客户端和服务器能在网络中通信,那必须得使用 Socket 编程,它是进程间通信里比较特别的方式,特别之处在于它是可以跨主机间通信。 Socket 的中文名叫作插口,咋一看还挺迷惑的。事实上,双方要…

基于SpringBoot图书进销存管理系统

采用技术 基于SpringBoot图书进销存管理系统的设计与实现~ 开发语言:Java 数据库:MySQL 技术:SpringBootMyBatis 工具:IDEA/Ecilpse、Navicat、Maven 页面展示效果 用户信息管理 图书类型管理 商品退货管理 客户信息管理 图…

软件测试相关内容第六弹 -- 测试实战

写在前:hello大家早中晚上好!这里是西西,前面我们已经学习了关于测试相关基础的介绍,点击链接直达前方内容~ 测试内容博客链接初识软件测试点击跳转软件测试相关概念点击跳转测试生命周期、BUG、测试大体流程点击跳转测试用例、测…

技术整理:SpringBoot+Redis+lua脚本防止超卖

SpringBootredislua 防止超卖 一、背景 工作中遇到了有人用 RedisTemplate 的 increment去做总库存的加减,但是这种方式是保证不了原子性的还是会超卖。 redis 是可以保证原子性,但是 RedisTemplate 里面的方法去调用redis是不能保证原子性 二、优化…

数据结构·排序

1. 排序的概念及运用 1.1 排序的概念 排序:排序是将一组“无序”的记录序列,按照某个或某些关键字的大小,递增或递减归零调整为“有序”的记录序列的操作 稳定性:假定在待排序的记录序列中,存在多个具有相同关键字的记…

在MySQL中,如何处理主键冲突的问题?

在MySQL中处理主键冲突的问题时,有几种常用的方法: 1. INSERT IGNORE - 使用 INSERT IGNORE 语句插入数据时,如果主键冲突(即主键已经存在于表中),MySQL将忽略此次插入操作,不会更改现有记录&am…

day03_mysql_课后练习 - 参考答案

文章目录 day03_mysql_课后练习mysql练习题第1题第2题第3题第4题第5题 day03_mysql_课后练习 mysql练习题 第1题 案例: 1、创建一个数据库:day03_test01_school 2、创建如下表格 表1 Department表的定义 字段名字段描述数据类型主键外键非空唯一D…

Docker 笔记(七)--打包软件生成镜像

目录 1. 背景2. 参考3. 文档3.1 使用docker container commit命令构建镜像3.1.1 [Docker官方文档-docker container commit](https://docs.docker.com/reference/cli/docker/container/commit/)Description(概述)Options(选项)Exa…

【Redis】Redis特性

Redis 认识redisRedis特性在内存中存储数据可编程可扩展性持久化Clustering高可用性 认识redis Redis,英文全称是Remote Dictionary Server(远程字典服务),是一个开源的使用ANSIC语言编写、支持网络、可基于内存亦可持久化的日志…

【JAVA】封装与包

。何为封装呢?简单来说 就是套壳屏蔽细节 封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行 交互 访问限定符 public:可以理解为一个人的外貌特征,谁都可以…