C语言实现双向链表

1.版本一

由于节点之间的连接变多 所以我们最好提前将前驱节点和后继节点用变量保存下来 以免等下在进行节点之间的指向时出错

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// 节点类
typedef struct Node {// 数据域int data;// 指针域struct Node* next;struct Node* pre;
}Node;// 别名
// 初始化链表
Node* initList() {Node* list = (Node*)malloc(sizeof(Node));list->data = 0;list->next = NULL;list->pre = NULL;return list;
}
// 头插法
void headInsert(int data, Node* list) {Node* node = (Node*)malloc(sizeof(Node));Node* pre = list;Node* next = list->next;node->data = data;pre->next = node;node->pre = pre;node->next = next;if (list->data != 0) {next->pre = node;}// 更新链表长度list->data++;
}
// 尾插法
void tailInsert(int data, Node* list) {// 为待插入节点分配内存Node* node = (Node*)malloc(sizeof(Node));// 获取待插入节点的前驱节点 前驱节点其实就是原来的尾节点Node* pre = list->next;while (pre->next) {pre = pre->next;}Node* next = pre->next;node->data = data;pre->next = node;node->pre = pre;node->next = next;// 更新链表长度list->data++;
}
// 删除方法
bool delete(int data, Node* list) {// 我们需要做的是删除指定节点值对应的第一个节点Node* cur = list->next;while (cur) {// 如果一旦遇到符合指定节点值的节点的话 那么就执行相关操作if (cur->data == data) {// 需要先考虑头删、尾删和中间删以及删除之后链表为空四种情况 总结之后 我们可以发现尾删和删除之后链表为空同属一种情况 都是只需要设置一条线即可 即前驱指向后继 但是其他情况需要设置两条线 即前驱指向后继 后继指向前驱cur->pre->next = cur->next;if (cur->next) {cur->next->pre = cur->pre;}free(cur);list->data--;return true;}cur = cur->next;}return false;
}
// 打印链表方法
void printList(Node* list) {Node* cur = list->next;while (cur) {printf("%d ", cur->data);cur = cur->next;}printf("\n");
}
int main() {Node* list = initList();headInsert(1, list);headInsert(2, list);headInsert(3, list);tailInsert(4, list);tailInsert(5, list);printList(list);// 3 2 1 4 5if (delete(5, list) == true) {printf("删除成功\n");}else {printf("删除失败\n");}printList(list);// 2 1 4 5
}

2.版本二(mj版本的双向链表 带有虚拟头节点)

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define ELEMENT_NOT_FOUND -1
// 我们的设计理念就是引入一个虚拟头节点 但是不引入头节点的标识以及尾节点的标识
// 节点类
typedef struct Node {int data;// 数据域struct Node* pre;struct Node* next;// 指针域
}Node;
// 初始化链表
Node* initList() {Node* list = (Node*)malloc(sizeof(Node));list->data = 0;list->pre = NULL;list->next = NULL;return list;
}
// 索引越界的处理
void outOfBounds(int index) {printf("索引为%d 索引越界了\n", index);
}
// 边界检查
void rangeCheck(int index, Node* list) {if (index < 0 || index >= list->data)outOfBounds(index);
}
// 针对添加方法的边界检查
void rangeCheckForAdd(int index, Node* list) {if (index < 0 || index > list->data)outOfBounds(index);
}
// 根据指定索引获取节点
Node* node(int index, Node* list) {// 对参数索引进行边界检查rangeCheck(index, list);Node* cur = list->next;for (int i = 0; i < index; ++i) {cur = cur->next;}return cur;
}
// 添加方法
void add(int index, int data, Node* list) {// 需要分成三种情况进行分析 一种情况是中间插入 需要设置四条线 一种情况是尾插 需要设置三条线 一种情况是头插 需要设置四条线 一种情况是对空链表进行插入操作 需要设置三条线 所以我们可以分成两种情况 一种是尾插的特殊情况 一中是其他位置插入// 对参数索引进行边界检查rangeCheckForAdd(index, list);// 为待插入节点分配内存Node* cur = (Node*)malloc(sizeof(Node));// 获取待插入节点的前驱节点 如果是头插的话 那么就需要特殊处理了Node* pre = index == 0 ? list : node(index - 1, list);// 获取待插入节点的后继节点Node* next = pre->next;cur->data = data;// 四种情况都要设置三条线 pre->next = cur;cur->pre = pre;cur->next = next;if (next)next->pre = cur;// 无论执行的是哪一种情况 都需要更新链表长度list->data++;
}
// 删除方法
int delete(int index, Node* list) {// 我们需要将问题分成三种情况去讨论 一种情况是中间删除 一种情况是尾删 一种情况是头删 如果是中间删除的话 那么我们需要操作的指针有两条 分别是前驱节点的后继节点还有后继节点的前驱节点 如果是尾删的话 那么就需要操作一条 即前驱节点指向后继节点 如果是头删的话 那么需要操作两条 同中间删除一样 如果是删除之后链表为空的话 那么就归结到尾删的情况中去考虑即可 并且复用尾删的代码Node* cur = node(index, list);// 我们保存一下待删除节点的节点值int delete = cur->data;// 通过待删除节点获取到他的前驱节点以及后继节点Node* pre = cur->pre;Node* next = cur->next;pre->next = next;if (next)next->pre = pre;// 我们还要去释放一下cur的内存 同时也解决了从cur指出的两条指针free(cur);// 更新一下链表的长度list->data--;// 返回待删除节点的节点值return delete;
}
// 定义一个方法 用于获取指定节点值对应的位置
int indexOf(int data, Node* list) {Node* cur = list->next;for (int i = 0; i < list->data; ++i) {if (cur->data == data)return i;cur = cur->next;}// 如果上述循环没有返回值的话 那么说明没有找到指定的节点值对应的节点return ELEMENT_NOT_FOUND;
}
// 获取指定索引处的节点值
int get(int index, Node* list) {return node(index, list)->data;
}
// 重置指定位置处的节点值
int set(int index, int newData, Node* list) {int data = node(index, list)->data;node(index, list)->data = newData;return data;
}
// 清空方法
void clear(Node* list) {Node* cur = list->next;Node* next;for (int i = 0; i < list->data; ++i) {next = cur->next;free(cur);cur = next;}// 清空链表之后链表长度为0list->data = 0;
}
// 判断链表是否包含指定节点值
bool contains(int data, Node* list) {return indexOf(data, list) != ELEMENT_NOT_FOUND;
}
// 判断链表是否为空
bool isEmpty(Node* list) {return list->data == 0;
}
// 获取链表长度
int size(Node* list) {return list->data;
}
// 打印链表的方法
void printList(Node* list) {Node* cur = list->next;while (cur) {printf("%d ", cur->data);cur = cur->next;}printf("\n");
}
// 定义一个主函数
int main() {// 创建一个链表Node* list = initList();// 头部添加 尾部添加add(0, 1, list);// 1add(0, 2, list);// 2 1add(0, 3, list);// 3 2 1add(0, 4, list);// 4 3 2 1add(size(list), 5, list);// 4 3 2 1 5// 打印链表printList(list);// 4 3 2 1 5// 头部删除 尾部删除delete(0, list);// 3 2 1 5delete(size(list) - 1, list);// 3 2 1// 打印链表printList(list);// 3 2 1// 获取头部元素printf("%d\n", get(0, list));// 3// 重置头部元素printf("%d\n", set(0, 4, list));// 3// 打印链表printList(list);// 4 2 1printf("%d\n", contains(4, list));// trueprintf("%d\n", isEmpty(list));// false// 清空链表clear(list);printf("%d\n", size(list));// 0return 0;
}

测试结果显示 这个代码符合预期

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

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

相关文章

【python playwright 安装及验证】

python playwright pip install playwright pip install playwright -i http://mirrors.aliyun.com/pypi/simple/ playwright codegen -o script.py -b chromium --ignore-https-errors --viewport-size “2560,1440” --proxy-server “http://100.8.64.8:60497” https://w…

Harbor安装

采用原生的方式安装Harbor 下载Harbor安装包&#xff1a;https://github.com/goharbor/harbor/releases/download/v2.3.4/harbor-offline-installer-v2.3.4.tgz 拖拽到Linux并解压&#xff1a; tar -zxvf harbor-offline-installer-v2.3.4.tgz -C /usr/local/修改Harbor配置文…

webpack的性能优化(二)——减少打包体积

优化webpack性能时&#xff0c;主要集中在两个方面&#xff1a;优化构建后的结果和优化构建时的速度。前一篇文章已经介绍了如何通过webpack的分包来优化构建后的结果。而在本篇文章中&#xff0c;我们将从减少打包体积的角度来探讨。 1.通过CDN链接引入第三方库 CDN是指通过相…

基于Python编程实现简单网络爬虫实现

引言 网络爬虫&#xff08;英语&#xff1a;web crawler&#xff09;&#xff0c;也叫网络蜘蛛&#xff08;spider&#xff09;&#xff0c;是一种用来自动浏览万维网的网络机器人。其目的一般为编纂网络索引。 --维基百科 网络爬虫可以将自己所访问的页面保存下来&#xff0c…

python 通过定时任务执行pytest case

这段Python代码使用了schedule库来安排一个任务&#xff0c;在每天的22:50时运行。这个任务执行一个命令来运行pytest&#xff0c;并生成一个报告。 代码开始时将job_done变量设为False&#xff0c;然后运行预定的任务。一旦任务完成&#xff0c;将job_done设置为True并跳出循…

Netty-Netty实现自己的通信框架

通信框架功能设计 功能描述 通信框架承载了业务内部各模块之间的消息交互和服务调用&#xff0c;它的主要功能如下&#xff1a; 基于 Netty 的 NIO 通信框架&#xff0c;提供高性能的异步通信能力&#xff1b; 提供消息的编解码框架&#xff0c;可以实现 POJO 的序列化和反…

AI智能分析网关V4烟火检测算法解决方案

一、背景需求 根据国家消防救援局公布的数据显示&#xff0c;2023年共接报处置各类警情213.8万起&#xff0c;督促整改风险隐患397万处。火灾危害巨大&#xff0c;必须引起重视。传统靠人工报警的方法存在人员管理难、场地数量多且分散等问题&#xff0c;无法有效发现险情降低…

【量化交易故事】小明开启了量化创业之旅-01

故事开始于2023年的春天&#xff0c;小明是一位对金融市场充满热情的IT工程师。在经历了数次基于主观判断和个人情绪进行投资却收获平平后&#xff0c;他意识到传统交易方式中的人为因素难以避免&#xff0c;而这往往成为影响投资决策稳定性和准确性的关键障碍。在一次偶然的机…

以太网抓包软件Wireshake应用介绍( SMART PLC MODBUSTCP通信)

首先介绍下常看到的字符ACK,ACK是确认字符,在数据通信中,接收站发给发送站的一种传输类控制字符,表示发来的数据已确认接收无误。在TCP/IP协议中,如果接收方成功的接收到数据,会回复一个ACK数据。通常ACK信号有自己固定的格式,长度大小,由接收方回复给发送方。ACK在TCP的…

dcm数据格式转nrrd数据格式(2维转3维)

目的 将dcm数据格式&#xff08;2D&#xff09;转成nrrd数据格式&#xff08;3D&#xff09; 将一个文件夹下的dcm数据转成一个nrrd数据 代码 1. 安装必要包 pip install SimpleITK2. 上代码 Descripttion: Result: Author: Philo Date: 2024-01-10 14:25:49 LastEditors: …

【MySQL】基础篇

文章目录 一、SQL规则与规范二、基本的SELECT语句SELECT...FROM...;列的别名 AS ""去除重复行 DISTINCT空值参与运算 结果一定也为NULL着重号 常量描述表结构 DESCRIBE过滤数据 WHERE 三、运算符算术运算符比较运算符非符号类型运算符逻辑运算符运算符优先级 四、排序…

前端js写数据结构与算法

1、什么是数据结构与算法 数据结构&#xff1a;是指数据对象中数据元素之间的相互关系。包括集合结构、线性结构、树形结构、图形结构。 算法&#xff1a;解决问题的思路。 2、时间复杂度 1.是什么? 执行当前算法所“花费的时间” 2.干什么? 在写代码的过程中&#xf…

浅学Linux之旅 day1 学习路线及计算机入门知识介绍

我不要做静等被掀起的轻波&#xff0c;我要生起翠绿的斑驳 偶尔过季的遭遭人事化长风拂过 思绪撕碎点燃了火 ——24.1.14 一、Linux学习路线 ①计算机入门知识介绍 ②Linux系统概述 ③Linux系统的安装和体验 ④Linux的网络配置和连接工具 ⑤Linux的目录结构 ⑥Linux的常用命令 …

【docker笔记】docker镜像

是什么 是一种轻量级、可执行的独立软件包&#xff0c;它包含运行某个软件所需的所有内容&#xff0c;我们把应用程序和配置依赖打包好形成一个可交付的运行环境&#xff08;包括代码、运行时需要的库、环境变量和配置文件等&#xff09;&#xff0c;这个打包好的运行环境就是…

C#用Enumerable.Range(Int32, Int32) 方法计算亲密数对

目录 一、定义 1. 因子分解 2.亲密数对 二、实例 1.Main()及运行结果 2.类库 一、定义 1. 因子分解 将给定的合数分解为素数的乘积。对于较小的正整数可借助于素数表用试除的方法求出其因子分解式。 2.亲密数对 如果说n和m是一个亲密数对&#xff0c;那么它们必须满足以…

HarmonyOS应用开发学习笔记 arkTS自定义弹窗(CustomDialog)简单使用 arkTS弹出框回调、监听

HarmonyOS应用开发学习笔记 arkTS自定义弹窗&#xff08;CustomDialog&#xff09;简单使用 1、CustomDialog装饰器用于装饰自定义弹框 1、定义弹出框 CustomDialog CustomDialog export struct CustomDialogExample {controller: CustomDialogControllerbuild() {Column() {…

mac vscode latex实用

网上有教程怎么在vscode里安装macTex以及插件&#xff0c;然后就可以在latex里写代码了&#xff0c;这里需要修改的是对应的json文件&#xff0c;输入command P,可以看到最近打开的json设置文件&#xff0c;结果如下 然后设置这个json文件&#xff0c;我的json文件设置如下 …

【MQTT】MQTT协议与指令下发;MQTT与Kafka比较

本文内容&#xff1a; 一、MQTT协议1. 发布/订阅模型&#xff1a;2. 连接和会话&#xff1a;3. QoS级别&#xff08;Quality of Service Levels&#xff09;&#xff1a;4. 保留消息&#xff1a;5. Last Will and Testament&#xff08;遗嘱消息&#xff09;&#xff1a;6. 适用…

springcloud微服务分布式 springboot+vue的轻院校园网购商城管理系统 Eureka

本文的研究目标是以商城的轻院网购商城管理体系为对象&#xff0c;论文的研究内容包括&#xff1a;商品信息、系统公告等方面进行了研究。系统以当前应用最为广泛的Java语言为基础&#xff0c;结合了目前应用最为广泛的嵌入式嵌入式平台&#xff0c;集成了B/S体系结构。数据库选…

Tensor Core的一些概念理解

英伟达的GPU产品架构发展如下图&#xff0c;Tensor Core是从2017年的Volta架构开始演变的针对AI模型大量乘加运算的特殊处理单元。本文主要梳理一些关于Tensor Core的一些基础概念知识。 什么是混合精度&#xff1f; 混合精度在底层硬件算子层面&#xff0c;使用半精度&#xf…