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语言代码示例)
一、算法介绍 冒泡排序是一种简单的排序算法,其核心思想是重复地遍历待排序列表,比较并交换相邻元素,使得较大的元素逐渐“冒泡”到列表的末尾,而较小的元素则逐渐上浮至列表的前端。该算法的名字源于类比元素的移动过程ÿ…...
OpenClaw多语言支持:Qwen2.5-VL-7B跨语种图文处理技巧
OpenClaw多语言支持:Qwen2.5-VL-7B跨语种图文处理技巧 1. 为什么需要多语言图文处理 上周我收到一份混合了英文技术文档和中文注释的项目资料,需要整理成统一格式的双语对照版本。手动复制粘贴到翻译工具再调整排版,花了我整整三个小时。这…...
Flash Memory技术解析与应用实践
1. Flash Memory技术全景解析作为一名嵌入式系统开发工程师,我使用Flash Memory已有十余年经验。从早期的NOR Flash烧录到现在的TLC NAND优化,这项技术始终是存储领域的核心支柱。让我们抛开教科书式的定义,从实际工程角度重新认识这项既熟悉…...
Windows平台OpenClaw部署:百川2-13B-4bits量化版调用详解
Windows平台OpenClaw部署:百川2-13B-4bits量化版调用详解 1. 为什么选择这个组合? 去年冬天,当我第一次尝试在Windows笔记本上部署本地AI助手时,遇到了显存不足的难题。我的GTX 3060显卡根本无法承载常规的13B模型,直…...
Ubuntu 24.04 内核 Kernel Panic 问题排查与解决流程(第二次出现该问题后,永久性解决)
问题描述 系统更新后重启,出现以下错误: Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)系统无法正常启动。问题原因分析 错误含义 内核在启动过程中无法找到并挂载根文件系统。unknown-block(0,0) 表示内核完全不知道…...
1220亿美元!OpenAI创下史上最大融资纪录;DeepSeek连续三天发生服务异常;Claude Code 51万行源码泄露 | 极客头条
「极客头条」—— 技术人员的新闻圈!CSDN 的读者朋友们好,「极客头条」来啦,快来看今天都有哪些值得我们技术人关注的重要新闻吧。(投稿或寻求报道:zhanghycsdn.net)整理 | 苏宓出品 | CSDN(ID&…...
零基础入门AI开发:在快马平台亲手制作你的第一个口播智能体
最近在尝试入门AI开发,发现用InsCode(快马)平台做"旗博士口播智能体"特别适合零基础选手。这个项目不需要自己从头写代码,但能完整走通AI应用开发全流程,分享下我的学习笔记: 项目整体结构 整个项目分三部分:…...
AI 视频生成美女跳舞测评 | 顶级 Prompt实测版(Grok Imagine、Kling AI 3.0、Veo 3.1)
兄弟们,AI 视频生成已经卷到飞起了!之前写小黄文靠grok,现在生成“美女舞蹈”视频也得靠它。 今天上手实测截至今天热门的3款视频生成工具,专攻“美女跳舞”这个高难度场景:动作流畅度、人物一致性、性感画面感、提示…...
GitHub功能多元拓展,korb工具革新REWE购物流程
【导语:GitHub提供了涵盖AI代码创作、开发者工作流、应用程序安全等多方面的丰富功能,同时推出不同规模和用例的解决方案。而korb命令行工具则为REWE超市购物带来新体验,可实现自动化购物流程。】GitHub:功能全面的开发者平台GitH…...
从零开始:Java使用通用物体识别-ResNet18镜像实现图像分类
从零开始:Java使用通用物体识别-ResNet18镜像实现图像分类 你是否想过,用Java写几行代码,就能让程序看懂一张图片里有什么?过去,这可能需要搭建复杂的Python环境、学习深度学习框架、处理繁琐的模型部署。但现在&…...
管道应力理论(应用)
本文仅对管道应力涉及的理论知识(偏向于应用)进行简单介绍。管道应力:对管道应力校核是为了防止管壁内应力过大对管道造成破坏,不同的荷载引起不同类型的应力,在实际工程应用中,一般分为三种:一…...
