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;
}
测试结果显示 这个代码符合预期
相关文章:
C语言实现双向链表
1.版本一 由于节点之间的连接变多 所以我们最好提前将前驱节点和后继节点用变量保存下来 以免等下在进行节点之间的指向时出错 #include <stdio.h> #include <stdlib.h> #include <stdbool.h> // 节点类 typedef struct Node {// 数据域int data;// 指针域…...
OpenGL 网格拾取坐标(Qt)
文章目录 一、简介二、代码实现三、实现效果参考资料一、简介 有时候我们希望通过鼠标来拾取某个网格中的坐标,这就涉及到一个很有趣的场景:光线投射,也就是求取一条射线与网格的交点,这里如果我们采用普通遍历网格中的每个面片的方式,当网格的面片数据量很大时计算效率就…...
GitHub高级搜索技巧
GitHub高级搜索技巧 in:name <关键字> 仓库名称带关键字查询 in:description <关键字> 仓库描述带关键字查询 in:readme <关键字> README文件带关键字查询 stars(fork): >() <数字> <关键字> star或fork数大于(或等于)指定数字的带关键字查…...
docker-compose安装HertzBeat赫兹跳动监控H3C交换机
前面我们用docker方式安装了HertzBeat,现在我们自己写个docker-compose.yml文件、创建文件直接docker-compose up -d直接启动运行 使用docker-compose需要先安装docker和docker-compose1、输入以下两段命令 mkdir 123 && cd 123 && mkdir data &a…...
NetSuite学习笔记 - 中心
一、什么是中心? 对于每个用户,NetSuite 会根据用户的指定角色显示一组可变的标签页面,称为中心。通俗来讲呢,NetSuite的中心其实就是我们常说的“导航菜单”。 只是在我过去常见的系统中,导航菜单一般都是固定的&am…...
鸿蒙开发笔记(三):页面和自定义组件生命周期
先明确自定义组件和页面的关系: 自定义组件:Component装饰的UI单元,可以组合多个系统组件实现UI的复用。 页面:即应用的UI页面。可以由一个或者多个自定义组件组成,Entry装饰的自定义组件为页面的入口组件,…...
报名活动怎么做_小程序创建线上报名活动最详细攻略
报名活动怎么做:一篇让你掌握活动策划与营销的秘籍 在当今社会,无论是线上还是线下,活动已经成为企业营销和品牌推广的重要手段。但是,如何策划一场成功的活动呢?这篇文章将为你揭示活动策划与营销的秘籍,…...
Apache POI 导出Excel报表
大家好我是苏麟 , 今天聊聊Apache POI . Apache POI 介绍 Apache POI 是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是,我们可以使用 POI 在 Java 程序中对Miscrosoft Office各种文件进行读写操作。 一般情况下,POI 都是用于操作 E…...
使用Qt连接scrcpy-server控制手机
Qt连接scrcpy-server 测试环境如何启动scrcpy-server1. 连接设备2. 推送scrcpy-server到手机上3. 建立Adb隧道连接4. 启动服务5. 关闭服务 使用QTcpServer与scrcpy-server建立连接建立连接并视频推流完整流程1. 开启视频推流过程2. 关闭视频推流过程 视频流的解码1. 数据包协议…...
debian12部署Gitea服务之二——部署git-lfs
Debian安装gitlfs: 先更新下软件包版本 sudo apt update 安装 sudo apt install git-lfs 验证是否安装成功 git lfs version cd到Gitea仓库目录下 cd /mnt/HuHDD/Git/Gitea/Repo/hu/testrepo.git 执行lfs的初始化命令 git lfs install客户机Windows端在官网下载并安装Git-Lfs 再…...
leetcode 1两数之和
题目 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 你可以按任意顺…...
C++多线程学习[三]:成员函数作为线程入口
一、成员函数作为线程入口 #include<iostream> #include<thread> #include<string>using namespace std;class Mythread { public:string str;void Test(){cout << str << endl;} }; int main() {Mythread test;test.str "Test";thr…...
移动硬盘无法识别处理办法
今天这里做一下总结,我现在手上有一个移动硬盘,插入win10电脑是有盘号的,但是 但是点击就出问题 解决办法 安装DiskGenius 下载网址在https://www.diskgenius.cn/download.php 下载之后解压安装就行,非常简单,然后…...
【Spring Cloud】Sentinel流量限流和熔断降级的讲解
🎉🎉欢迎来到我的CSDN主页!🎉🎉 🏅我是Java方文山,一个在CSDN分享笔记的博主。📚📚 🌟推荐给大家我的专栏《Spring Cloud》。🎯🎯 &am…...
前端浮点和16进制互转
一、浮点转16进制数据 //浮点数转16进制 function singleToHex(t) {if (t "") {return "";}t parseFloat(t.substr(0, 4));if (isNaN(t) true) {return "Error";}if (t 0) {return "00000000";}var s,e,m;if (t > 0) {s 0;}e…...
Java中hashCode()与equals()的相关规定
API文件有对对象的状态制定出必须遵循的规则。hashCode()和equals()是object中定义的两个方法,它们都与对象的相等性有关。 通常情况下我们需要同时使用这两个方法来判断两个对象是否相等,只有两个对象的equals()方法返回true,并且它们的has…...
转行做鸿蒙开发首先需要学习哪些?
随着越来越多的企业和团队开始布局鸿蒙生态,鸿蒙开发人才的需求也呈现出井喷式的增长。对于开发者而言,掌握鸿蒙开发技能不仅意味着能够抓住这个千载难逢的机遇,更意味着能够在未来的科技竞争中占据先机。 在这个变革的时代,鸿蒙开…...
8x8离散余弦的快速精确实现使用数据流单指令多数据扩展指令集进行转换MMX 说明书
1.https://www.cs.cmu.edu/~barbic/cs-740/ap922.pdf 2.FFmpeg: libavcodec/x86/fdct.c Source File 再学FDCT快速精确实现协议改写浮点FDCT, ffmpeg的dct使用的就是这个快速精确协议。 3.http://dspace.fcu.edu.tw/bitstream/2377/30265/1/ICM%204-1.pdf 我想如把所有余弦…...
微信公众号注册(详细图文教程)
目录 一、公众号注册准备1.1 准备事项1.2 个人注册1.3 企业注册 二、公众号注册2.1 基本信息填写2.2 选择类型2.3 信息登记2.4 公众号信息2.5 修改头像2.6 自动回复消息 三、总结 一、公众号注册准备 1.1 准备事项 公众号名称:公众号名称可以由中文、英文、数字、…...
排序算法-冒泡排序(含C语言代码示例)
一、算法介绍 冒泡排序是一种简单的排序算法,其核心思想是重复地遍历待排序列表,比较并交换相邻元素,使得较大的元素逐渐“冒泡”到列表的末尾,而较小的元素则逐渐上浮至列表的前端。该算法的名字源于类比元素的移动过程ÿ…...
循环冷却水流量示意图设计 建筑水流量示意图绘制教程
一、引言 在建筑给排水、暖通空调及工业循环水系统设计中,循环冷却水流量示意图与建筑水流量示意图是核心技术图纸之一,其作用是直观呈现水流路径、管径规格、流量分配、设备连接关系及压力节点参数,为系统施工、调试、运维及故障排查提供可…...
嵌入式软件框架设计:从基础到实战
1. 嵌入式软件框架设计基础作为一名在嵌入式领域摸爬滚打多年的工程师,我深刻体会到框架设计对项目成败的决定性影响。嵌入式系统与通用计算机系统最大的区别在于其资源受限性和实时性要求,这就决定了我们不能简单套用桌面开发的思维模式。程序框架本质上…...
Phi-4-mini-reasoning助力Java安装与环境配置:从JDK到IDE的智能指引
Phi-4-mini-reasoning助力Java安装与环境配置:从JDK到IDE的智能指引 1. 为什么需要智能指引来安装Java? 刚接触Java开发的朋友们,十有八九会在环境配置这一步卡壳。我见过太多初学者在JDK版本选择、环境变量配置这些环节反复折腾࿰…...
数字孪生+AI:某国家级技术科研机构:耦合仿真评估部件性能,长期运维监测承压状态
部件仿真|设备安全|能源装备|风险评估 某国家级技术科研机构长期服务于国家级重点工程与大型产业体系,在复杂系统运行保障、风险评估与技术支撑等方面承担着关键角色。其业务覆盖多类型基础设施与工程场景,具备完善的…...
基于S7-200 PLC与组态王技术的温室大棚控制方案:包含梯形图原理图、IO分配及组态画面详解
基于S7-200 PLC和组态王温室大棚控制 我们主要的后发送的产品有,带解释的梯形图接线图原理图图纸,io分配,组态画面菜农张叔上周还给我打电话吐槽:“小王啊,上周那场降温加突然转晴,我三点爬起来盖半层棉被…...
EmbeddingGemma-300m部署指南:Ollama镜像+Prometheus监控+日志追踪一体化
EmbeddingGemma-300m部署指南:Ollama镜像Prometheus监控日志追踪一体化 想快速搭建一个功能强大、易于管理的文本向量化服务吗?EmbeddingGemma-300m作为谷歌推出的轻量级嵌入模型,凭借其3亿参数和出色的性能,是构建本地语义搜索、…...
别再傻傻分不清HIL和SIL了!用NI PXI和Simulink手把手教你搭建第一个测试环境
从零开始搭建HIL/SIL测试环境:NI PXI与Simulink实战指南 刚接触在环测试的工程师常常被各种术语搞得晕头转向——HIL、SIL、MIL,它们到底有什么区别?更重要的是,接到一个控制器测试任务时,该如何从零开始搭建测试环境&…...
在WinForm里玩转Halcon 3D点云:从C#代码导出到完整UI显示的保姆级避坑指南
在WinForm里玩转Halcon 3D点云:从C#代码导出到完整UI显示的保姆级避坑指南 当工业视觉项目需要处理复杂的三维场景时,Halcon的3D点云处理能力往往成为开发者的首选。但将Halcon的强大算法无缝集成到C# WinForm应用中,却可能遭遇一系列"…...
uboot移植实战:DDR初始化参数优化与调试指南
1. 理解DDR初始化在uboot移植中的重要性 第一次接触uboot移植时,我完全不明白为什么DDR初始化这么麻烦。直到有一次,我把开发板直接烧成砖头,才真正意识到这个环节有多关键。简单来说,DDR初始化就像是给电脑装内存条,但…...
Ubuntu 22.04上,用Cephadm 17.2.0搭建单节点Ceph集群的保姆级避坑指南
Ubuntu 22.04单节点Ceph集群实战:从零到生产级部署的17个关键细节 当你在Ubuntu 22.04上尝试用Cephadm搭建单节点Ceph集群时,是否遇到过这些场景:bootstrap卡在某个步骤超过半小时、OSD设备明明存在却显示"no available devices"、…...
