[C语言][数据结构][链表] 双链表的从零实现!

目录

零.必备知识

0.1 一级指针 && 二级指针

0.2 双链表节点的成员列表

        a. 数据

        b. 后驱指针

        c. 前驱指针

0.3 动态内存空间的开辟

一. 双链表的实现与销毁

        1.1 节点的定义

        1.2 双向链表的初始化 && 创建新节点

        1.3 尾插 

        1.4 头插 

        1.5 尾删 

        1.6 头删

        1.7 在指定位置之后插入数据 

        1.8 删除指定位置的数据 

        1.9 销毁双链表

 二.双链表源码

List.h

List.c


零.必备知识

0.1 一级指针 && 二级指针

0.2 双链表节点的成员列表

        a. 数据

        b. 后驱指针

        c. 前驱指针

0.3 动态内存空间的开辟


一. 双链表的实现与销毁

此次实现的链表是带头双向循环链表.如图:

        1.1 节点的定义

后驱节点指向写一个节点.

前驱节点指向前一个节点.

        1.2 双向链表的初始化 && 创建新节点

初始化是指:创建了一个哨兵节点.

这个哨兵节点的作用是:避免双向链表死循环.

 

        1.3 尾插 

贯穿本文的核心要点是:先修改新节点中前驱,后驱指针的指向.再修改其余节点前驱,后驱指针的指向.

        1.4 头插 

 

        1.5 尾删 

尾删和接下来的头删,都是可以创建一个临时指针来指向要删除的节点.

这样看以来更直观,更重要的是方便进行空间的释放.

 

        1.6 头删

 

 

        1.7 在指定位置之后插入数据 

在指定位置之后插入数据:可以先实现一个查找函数,来自己指定pos.

 

        1.8 删除指定位置的数据 

 

        1.9 销毁双链表

 注意:此处传入的是一级指针,并没有真正的修改phead(哨兵位/头节点)中的值,如果要真正的销毁双链表,需要传入二级指针.

那么为什么我传的是一级指针呢?  答案:保持传入参数的一致性.

 

 二.双链表源码

List.h

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>// 单个节点的定义
typedef int LTDateType;
typedef struct List
{LTDateType date;struct List* next; // 后驱节点struct List* prev; // 前驱节点
}List;
// 节点的创建
List* LTBuyNode(LTDateType x);
// 双向链表的初始化
List* LTInit();
// 双向链表的展示
void LTPrint(List* phead);
// 尾插-头插
void LTPushBack(List* phead, LTDateType x);
void LTPushFront(List* phead, LTDateType x);
// 尾删-头删
void LTPopBack(List* phead);
void LTPopFront(List* phead);
// 查找指定数据
List* LTFind(List* phead, LTDateType x);
// 在指定位置之后插入数据
void LTInsertAfter(List* pos, LTDateType x);
// 删除指定位置的数据
void LTErase(List* phead, List* pos);
// 销毁双链表
void LTDestroy(List* phead);

List.c

#define  _CRT_SECURE_NO_WARNINGS
#include "List.h"
// 节点的创建
List* LTBuyNode(LTDateType x)
{List* newNode = (List*)malloc(sizeof(List));if (newNode == NULL) { // 创建失败perror("malloc fail!");exit(1);}newNode->date = x;newNode->next = NULL;newNode->prev = NULL;return newNode;
}
// 双向链表的初始化
List* LTInit()
{List* newNode = LTBuyNode(-1);newNode->next = newNode;newNode->prev = newNode;return newNode;
}
// 双向链表的展示
void LTPrint(List* phead)
{assert(phead);List* pcur = phead->next;while (pcur != phead) {printf("%d->", pcur->date);pcur = pcur->next;}printf("\n");
}
// 尾插
void LTPushBack(List* phead, LTDateType x)
{assert(phead);// 创建新节点List* newNode = LTBuyNode(x);// 先更改新节点的前驱后驱指针newNode->next = phead;newNode->prev = phead->prev;// 更改其余节点的前驱后驱指针phead->prev->next = newNode;phead->prev = newNode;
}
// 头插
void LTPushFront(List* phead, LTDateType x)
{assert(phead);List* newNode = LTBuyNode(x);// 更改newNode的前驱后驱指针newNode->next = phead->next;newNode->prev = phead;// 更改其余节点的指针指向phead->next->prev = newNode;phead->next = newNode;
}
// 尾删
void LTPopBack(List* phead)
{assert(phead);List* pcur = phead->prev;// 更改要删除的节点的前一个节点的指针指向pcur->prev->next = phead;// 更改哨兵位的指针指向phead->prev = pcur->prev;// 释放尾节点free(pcur);pcur = NULL;
}
// 头删
void LTPopFront(List* phead)
{assert(phead);List* pcur = phead->next;phead->next = pcur->next;pcur->next->prev = phead;// 销毁pcur节点free(pcur);pcur = NULL;
}
// 查找指定数据
List* LTFind(List* phead, LTDateType x)
{assert(phead);List* pcur = phead->next;while (pcur != phead) {if (pcur->date == x) {printf("找到了!\n");return pcur;}pcur = pcur->next;}printf("没有找到!\n");return NULL;
}
// 在指定位置之后插入数据
void LTInsertAfter(List* pos, LTDateType x)
{assert(pos);List* newNode = LTBuyNode(x);// 修改新节点的指向newNode->next = pos->next;newNode->prev = pos;// 修改其余节点的指向pos->next->prev = newNode;pos->next = newNode;
}
// 删除指定位置的数据
void LTErase(List* phead, List* pos)
{assert(phead && pos);pos->next->prev = pos->prev;pos->prev->next = pos->next;// 销毁pos节点free(pos);pos = NULL;
}
// 销毁双链表
void LTDestroy(List* phead)
{assert(phead);List* pcur = phead->next;while (pcur != phead) {List* prev = pcur;pcur = pcur->next;// 销毁 pcur的前一个节点free(prev);prev = NULL;}// 销毁哨兵位free(phead);phead = NULL; // 但是没有真正的改变哨兵位, 因为传的是一级指针// 如果要真正的改变 phead的值,则需要传递二级指针
}

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

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

相关文章

MySQL8.0.36-社区版:错误日志(2)

mysql有个错误日志&#xff0c;是专门记录错误信息的&#xff0c;这个功能默认是开启的 一般都是在/var/log/mysqld.log 日志中存放 1.错误日志的位置 首先我们使用命令去查看一下&#xff0c;这个错误日志文件究竟在哪 进入到mysql中&#xff0c;使用命令 show variables…

二叉树遍历(前序创建|中序遍历)

牛客题目链接 目录 1.解题思路 1.1中序遍历打印 ​1.2前序创建二叉树 1.3注意点 博主这里用的是java实现 随手记一个知识: hasNext读取到空格或者换行符会结束 hasNextLine读取到换行符才会结束&#xff08;空格不会退出&#xff09; 为什么要强调这个呢&#xff1f; …

Vivado Design Suite中的增量实现和增量模式

Vivado Incremental&#xff08;增量&#xff09;是Xilinx FPGA设计工具中的一种功能&#xff0c;它允许对设计的一部分进行修改和重新编译&#xff0c;而不需要对整个设计进行重新编译。这种增量式的方法可以显著减少编译时间&#xff0c;特别是在进行小的修改或迭代开发时。 …

std::stringstream

std::stringstream 是 C 标准库中的一个类&#xff0c;用于对字符串进行输入输出操作&#xff0c;类似于文件流&#xff08;std::ifstream 和 std::ofstream&#xff09;。它允许你像使用 std::cin 和 std::cout 一样使用字符串。 std::stringstream 可以将字符串作为输入源&am…

ThreadPoolExecutor线程池解析

ThreadPoolExecutor线程池解析 一、ThreadPoolExecutor常见参数 jdk中Executors提供了几种常用的线程池&#xff0c;底层都是ThreadPoolExecutor。 public ThreadPoolExecutor(int corePoolSize,//核心线程数int maximumPoolSize,// 最大线程数long keepAliveTime,//非核心线程…

大数据产品有哪些分类?各类里知名大数据产品都有哪些?

随着互联网技术的持续进步和全球数字化转型的推进&#xff0c;我们正处于一个数据爆炸的时代。在这样的大背景下&#xff0c;大数据已经逐渐崭露头角&#xff0c;成为了推动各行各业发展的关键因素和核心资源。大数据不仅仅是指数据的规模巨大&#xff0c;更重要的是它蕴含的价…

量子信息产业生态研究(一):关于《量子技术公司营销指南(2023)》的讨论

写在前面。量子行业媒体量子内参&#xff08;Quantum Insider&#xff09;编制的《量子技术公司营销指南》是一本实用的英文手册&#xff0c;它旨在帮助量子科技公司建立有效的营销策略&#xff0c;同时了解如何将自己定位成各自的行业专家。本文对这篇指南的主要内容进行了翻译…

ubuntu上安装截图工具-Flameshot及其使用步骤说明

Flameshot 火焰截图-推荐 安装命令&#xff1a; sudo apt install flameshot 操作方式&#xff1a; 1&#xff09;打开 2&#xff09;右上角弹窗 3&#xff09;点击后弹出 选择进行截图 4&#xff09;截图后再选择分享 5&#xff09;再重新选择区域&#xff0c;出现编辑…

【蓝桥杯】2024年第15届真题题目

试题 A: 握手问题 本题总分&#xff1a; 5 分 【问题描述】 小蓝组织了一场算法交流会议&#xff0c;总共有 50 人参加了本次会议。在会议上&#xff0c; 大家进行了握手交流。按照惯例他们每个人都要与除自己以外的其他所有人进 行一次握手&#xff08;且仅有一次&a…

CMD 汉字乱码处理

windows 11 cmd汉字乱码问题处理 一 查看CMD编码 win R 输入 cmd 输入 chcp 查看回显信息 “936”代表的意思就是 GBK (汉字内码扩展规范)&#xff0c;通常情况下GBK也是cmd的默认编码。 解决乱码需要把编码改为 utf-8 二 临时修改 在 终端中输入 chcp 65001 三 永久修改…

蓝桥杯嵌入式第十五届省赛真题题目

蓝桥杯昨天也考完了&#xff0c;大家可以看看题目 客观题题目 程序题题目

【Python函数和类4/6】递归与匿名函数

目录 目标 匿名函数 多个形参 匿名函数的局限性 递归 语言例子 数学例子 递归的实现 递归代码 练习 总结 目标 在之前的博客中&#xff0c;我们学习了定义函数、调用函数以及设置函数的参数。在今天&#xff0c;我们会补充函数的两个常见的知识点&#xff0c;一个是匿…

学浪视频怎么缓存?

缓存学浪视频轻而易举&#xff01;推荐使用“小浪助手”&#xff0c;一款便捷的工具&#xff0c;助你轻松实现。工具已经预先打包好&#xff0c;需要的朋友可以自行下载。快试试&#xff0c;畅享学习吧&#xff01; 学浪下载器链接&#xff1a;https://pan.baidu.com/s/1y7vcq…

spring容器

spring容器 实现方式 spring中提供了各式各样的IOC容器的实现供用户选择和使用&#xff0c;使用什么样的容器取决于用户的需要 BeanFactory 该接口是最简单的容器&#xff0c;提供了基本的DI支持。最常用的BeanFactory实现是XmlBeanFactory类&#xff0c;根据XML文件中的定义加…

RetinalNet论文笔记

RetinalNet 概述1. 引言2. 相关工作3. 焦点损失4. RetinaNet Detector 检测器5. 实验6. 结论 3. Focal loss3.1. 平衡交叉熵3.2. 焦点损失定义3.3. 类别不平衡和模型初始化3.4. 类别不平衡和两阶段检测器 4. RetinaNet Detector特征金字塔网络骨干&#xff08;Feature Pyramid …

PostgreSQL入门到实战-第二十五弹

PostgreSQL入门到实战 PostgreSQL中表连接操作(九)官网地址PostgreSQL概述PostgreSQL中NATURAL JOIN命令理论PostgreSQL中NATURAL JOIN命令实战更新计划 PostgreSQL中表连接操作(九) 使用PostgreSQL NATURAL JOIN从两个表中查询数据。 官网地址 声明: 由于操作系统, 版本更新…

C++实现AVL树

文章目录 一、平衡树的优势二、二叉平衡搜索树的节点定义三、二叉搜索树的插入3.1 寻找插入位置3.2 开始判定平衡因子&#xff0c;平衡因子有变就开始旋转3.2.1 左旋的情况3.2.2 左旋代码&#xff08;一定要考虑平衡因子为2或者-2的节点是否有父节点&#xff09;3.2.2 右旋的情…

DC-5渗透测试复现

DC-5渗透测试复现 目的&#xff1a; 获取最高权限以及5个flag 过程&#xff1a; 信息打点-文件包含漏洞-弹shell- scren-4.0.5提权 环境&#xff1a; 攻击机&#xff1a;kali(192.168.85.136) 靶机&#xff1a;DC_3(192.168.85.134) 复现&#xff1a; 一.信息收集 nma…

PlantUML 实战示例(使用 PlantUML 画用例图、类图、活动图、时序图)

目录 前言 需求场景 用例图 类图 活动图 时序图 前言 在软件开发的生命周期中&#xff0c;需要先进行设计&#xff0c;最后才是进行具体的编码和测试。设计时就需要画各种 UML 图&#xff0c;有专业的 UML 画图软件&#xff0c;也有很多在线的 UML 画图网站可以来画图&a…

Dubbo面试回答简单版

一、dubbo特性 超时重试机制地址缓存多版本负载均衡&#xff1a;随机、权重轮询、最少活跃调用、一致性哈希集群容错&#xff1a;失败重试、快速失败、失败安全、失败自动恢复、并行调用、广播服务降级&#xff1a;异常时返回mock 集群容错 FailOver 失败重试&#xff0c;读…