【C语言】实现双向链表


目录

(一)头文件

(二) 功能实现

(1)初始化

 (2)打印链表

(3) 头插与头删

(4)尾插与尾删

(5)指定位置之后插入

(6)删除指定位置的数据

(7)链表的销毁


 

正文开始:

        在实际应用中,常用的双向链表是双向带头循环链表,本文考虑到实际应用,目的也是实现双向带头循环链表。

(一)头文件

        命名"List.h"

        本文不加解释的给出头文件,按照头文件来实现双向链表的基本功能:包括打印链表,链表的初始化,头插与头删,尾插与尾删,在指定位置之后插入数据,删除指定位置的数据,链表的销毁等共九个功能。

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//链表的数据类型
typedef int LTtype;
//双向链表的节点
typedef struct ListNode
{LTtype data;struct ListNode* prev;struct ListNode* next;
}LTNode;//双向链表带有哨兵位,插入数据之前先插入哨兵位
//初始化》传入头节点
void LTInit(LTNode** pphead);//初始化2>返回头节点
LTNode* LtInit0();//销毁链表
void LTDestory(LTNode** pphead);//尾插
void LTtail_push(LTNode* phead,LTtype x);//头插
//是在头节点之后插入,在头节点之前插入等价于尾插
void LThead_push(LTNode* phead,LTtype x);//打印便于调试
void Print(LTNode* phead);//头删
void LTpophead(LTNode* phead);//尾删
void LTpoptail(LTNode* phead);//查找
//为了找到pos位置
LTNode* LTfind(LTNode* phead, LTtype x);//在pos之后插入数据
void LTinsert(LTNode* pos, LTtype x);//删除pos处的数据
void LTerase(LTNode* pos);

(二) 功能实现

        带头相对于不带头有诸多优势:

带头与不带头链表:

        带头链表是指在链表的开头增加一个额外的节点,该节点不存储具体的数据,仅用于辅助操作链表。带头链表的带头节点可以简化链表的操作,提高代码的可读性和可维护性。

        意义:

        带头节点可以避免链表为空的特殊情况处理。在没有带头节点的链表中,如果链表为空,添加、删除等操作都需要额外的处理逻辑。而带头节点的链表中,带头节点一直存在,无论链表是否为空,操作逻辑都是一致的,可以减少代码的复杂性。

        局限:

        造成额外的空间浪费。

(1)初始化

         初始化有两种方法,具体实现如下:

传址初始化

        初始化传递链表的地址(二级指针【通过传址,将形参与实参建立联系】),初始化要插入头指针哨兵位(不保存有效的数据),便于简化代码逻辑。

//双向链表带有哨兵位,插入数据之前先插入哨兵位
void LTInit(LTNode** pphead)
{*pphead = (LTNode*)malloc(sizeof(LTNode));if (*pphead == NULL){perror("malloc");exit(1);}(*pphead)->data = -1;(*pphead)->next = (*pphead)->prev = *pphead;
}

接受返回值初始化

        在函数内申请头节点,最后返回到主函数内:

//初始化2>返回头节点
LTNode* LtInit0()
{LTNode* phead = (LTNode*)malloc(sizeof(LTNode));if (phead == NULL){perror("malloc");exit(1);}phead->data = -1;phead->next = phead->prev = phead;return phead;
}

 (2)打印链表

        以便于调试,理解代码;

        与打印单链表一样,不同的是:while的跳出条件是 pcur != phead;


//打印链表
void Print(LTNode* phead)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){printf("%d->", pcur->data);pcur = pcur->next;}printf("\n");
}

(3) 头插与头删

        插入操作需要申请新节点:


//获取新节点
LTNode* LTbuynewnode(LTtype x)
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){perror("malloc");exit(1);}newnode->data = x;newnode->next = newnode->prev = NULL;return newnode;
}

 

        头插与头删

         头插,是在头节点之后插入,在头节点之前插入等价于尾插;

         先对新节点进行操作,因为新节点与原节点没有联系,不会因为对指针进行操作而丢失节点,造成数据丢失和内存泄漏;

        我们可以直接得到phead,因此先让phead的下一个节点的前驱指针指向newnode,再改变phead的next指针;(如果反过来,先改变phead的next指向newnode,此时newnode后面的节点就找不到了。)


//头插,是在头节点之后插入,在头节点之前插入等价于尾插
void LThead_push(LTNode* phead, LTtype x)
{assert(phead);LTNode* newnode = LTbuynewnode(x);//先对新节点进行操作newnode->next = phead->next;newnode->prev = phead;phead->next->prev = newnode;phead->next = newnode;}

头删

        断言传过来的指针不为空,链表不为空,通过assert实现;

        通过创建del指针(要被删除的节点),保存旧头节点;

        通过创建next指针,保存旧头节点的next节点;

        这样命名后进行头删,逻辑清晰,不易出错;


//头删
void LTpophead(LTNode* phead)
{assert(phead);assert(phead->next != phead);LTNode* del = phead->next;//被删除的节点LTNode* next = del->next;//del的后置节点phead->next = next;next->prev = phead;free(del);del = NULL;
}

 

(4)尾插与尾删

尾插

        与头插的逻辑完全相同


//尾插
void LTtail_push(LTNode* phead, LTtype x)
{assert(phead);LTNode* newnode = LTbuynewnode(x);newnode->next = phead;newnode->prev = phead->prev;phead->prev->next = newnode;phead->prev = newnode;
}

尾删

        与头删的逻辑完全相同


//尾删
void LTpoptail(LTNode* phead)
{assert(phead);assert(phead->next != phead);LTNode* del = phead->prev;//被删除的节点LTNode* prev = del->prev;//del的前置节点prev->next = phead;phead->prev = prev;free(del);del = NULL;
}

 

(5)指定位置之后插入

查找

         查找数据值为x的节点,找到返回此节点,找不到返回NULL;


//查找
LTNode* LTfind(LTNode* phead,LTtype x)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}

 

 在指定位置之后插入数据

        根据find找到的pos位置,申请新节点,插入到pos之后


//在pos位置之后插入数据
void LTinsert(LTNode* pos,LTtype x)
{assert(pos);LTNode* newnode = LTbuynewnode(x);newnode->next = pos->next;newnode->prev = pos;pos->next->prev = newnode;pos->next = newnode;
}

 

(6)删除指定位置的数据

         删除指定位置的数据,需要的指针有pos的前驱节点,pos节点。


//删除pos处的数据
void LTerase(LTNode* pos)
{assert(pos);LTNode* prev = pos->prev;LTNode* next = pos->next;prev->next = next;next->prev = prev;free(pos);pos = NULL;
}

 

(7)链表的销毁

        链表的销毁共有两种方法:

传址销毁


//销毁链表
void LTDestory(LTNode** pphead)
{assert(pphead);//哨兵位不为空assert(*pphead);LTNode* pcur = (*pphead)->next;while (pcur != *pphead){LTNode* next = pcur->next;free(pcur);pcur = next;}//释放哨兵位free(pphead);//此时可以改变哨兵位的pphead = NULL;}

 传值销毁

        一级指针phead传递的是值,不是phead的地址,所以在函数中改变phead并不会改变phead的实际值
        函数返回后,仍需要手动phead = NULL;


//推荐使用一级,为了保持接口的一致性
void LTDestory0(LTNode* phead)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){LTNode* next = pcur->next;free(pcur);pcur = next;}free(phead);phead = NULL;//无效的置空,当做是习惯
}
//一级指针phead传递的是值,不是phead的地址,所以在函数中改变phead并不会改变phead的实际值
//函数返回后,仍需要手动phead = NULL;

 

完~

未经作者同意禁止转载 

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

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

相关文章

DMA直接内存访问,STM32实现高速数据传输使用配置

1、DMA运用场景 随着智能化、信息化的不断推进&#xff0c;嵌入式设备的数据处理量也呈现指数级增加&#xff0c;因此对于巨大的数据量处理的情况时&#xff0c;必须采取其它的方式去替CPU减负&#xff0c;以保证嵌入式设备性能。例如SD卡存储器和音视频、网络高速通信等其它情…

甘肃旅游服务平台:技术驱动的创新实践

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

R语言阈值效应函数cut.tab2.0版发布(支持线性回归、逻辑回归、cox回归,自定义拐点)

阈值效应和饱和效应是剂量-反应关系中常见的两种现象。阈值效应是指当某种物质的剂量达到一定高度时&#xff0c;才会对生物体产生影响&#xff0c;而低于这个剂量则不会产生影响。饱和效应是指当某种物质的剂量达到一定高度后&#xff0c;其影响不再随剂量的增加而增加&#x…

【开源】基于JAVA+Vue+SpringBoot的假日旅社管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统介绍2.2 QA 问答 三、系统展示四、核心代码4.1 查询民宿4.2 新增民宿评论4.3 查询民宿新闻4.4 新建民宿预订单4.5 查询我的民宿预订单 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的假日旅社…

编写代码(LLVM的第一个项目)

下面这个完整代码 它相对较短&#xff0c;因为它建立在LLVM 流程的基础设施上 后者替我们完成大部分工作 我们从程序使用cl命名空间中的llvm工具&#xff08;cl代表命令行&#xff09;来实现我们的命令行接口 需要调用ParseCommandLineOption函数声明cl&#xff1a;&#xff…

【ES】--Elasticsearch的分词器详解

目录 一、前言二、分词器原理1、常用分词器2、ik分词器模式3、指定索引的某个字段进行分词测试3.1、采用ts_match_analyzer进行分词3.2、采用standard_analyzer进行分词三、如何调整分词器1、已存在的索引调整分词器2、特别的词语不能被拆开一、前言 最近项目需求,针对客户提…

[C#]winform制作圆形进度条好用的圆环圆形进度条控件和使用方法

【创建圆形进度条流程】 在C# WinForms应用程序中创建一个圆形进度条&#xff08;通常用作仪表盘的显示&#xff09;可以通过多种方式实现。下面是一个简单的例子&#xff0c;演示如何使用System.Drawing命名空间中的图形绘制功能来绘制一个基本的圆形进度条。 首先&#xff0…

在vscode上传项目到gitee

一、在Gitee上新建一个仓库 Tip&#xff1a;若已经创建过了&#xff0c;直接跳到第二部分看VsCode如何上传代码到Gitee 创建仓库比较简单&#xff0c;下面两张图就是整个过程&#xff0c;这里不在赘述&#xff0c;具体如下&#xff1a; 二、VsCode连接Gitee上创建的仓…

第二篇【传奇开心果微博系列】Python微项目技术点案例示例:成语接龙游戏

传奇开心果微博系列 系列微博目录Python微项目技术点案例示例系列 微博目录一、微项目目标二、雏形示例代码三、扩展整体思路四、玩家输入示例代码五、成语判断示例代码六、回答判断示例代码七、电脑判断示例代码八、游戏结束示例代码九、界面优化示例代码十、扩展成语库示例代…

数据结构——6.1 图的基本概念

第六章 图 6.1 图的基本概念 概念 图的概念&#xff1a;G由点集V和边集E构成&#xff0c;记为G(V,E)&#xff0c;边集可以为空&#xff0c;但是点集不能为空 注意&#xff1a;线性表可以是空表&#xff0c;树可以是空树&#xff0c;但图不可以是空&#xff0c;即V一定是非空集…

【MATLAB】GA_BP神经网络回归预测算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 GA_BP神经网络回归预测算法结合了遗传算法&#xff08;Genetic Algorithm, GA&#xff09;和BP神经网络&#xff08;Backpropagation Neural Network, BPNN&#xff09;&#xff0c;用于解…

蓝桥杯嵌入式第8届真题(完成) STM32G431

蓝桥杯嵌入式第8届真题(完成) STM32G431 题目 分析和代码 对比第六届和第七届&#xff0c;这届的题目在逻辑思维上确实要麻烦不少&#xff0c;可以从题目看出&#xff0c;这届题目对时间顺序的要求很严格&#xff0c;所以就可以使用状态机的思想来编程&#xff0c;拿到类似题…

Python基于大数据的电影预测分析系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

介绍 MSTest Runner – CLI、Visual Studio 等

作者&#xff1a;Amaury Lev Marco Rossignoli Jakub Jareš 排版&#xff1a;Alan Wang 我们很高兴推出 MSTest 运行器&#xff0c;这是一款全新的轻量级 MSTest 测试运行器。这个新的运行器使测试更加便携和可靠&#xff0c;运行速度更快&#xff0c;并且具有可扩展性&#x…

leetcode 461. 汉明距离

比较简单的一题&#xff0c;先对两个整数进行异或操作&#xff0c;会将两个整数二进制形式中各个数字进行异或操作&#xff0c;不同的数字则为1&#xff0c;再通过移位操作统计得到的二进制数中为1的个数&#xff0c;即为所求。 Java代码如下&#xff1a; class Solution {pub…

Android SystemConfig相关

SystemConfig在哪里初始化 它声明在PackageManagerService类的静态方法main()中。在该方法中间定义Injector类对象时&#xff0c;作为它的构造参数。它是调用的SystemConfig.getInstance()实现初始化&#xff0c;之后能通过Injector类对象的getSystemConfig()得到SystemConfig类…

计算机网络——网络安全

计算机网络——网络安全 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff0c; [跳转到网站](https://www.captainbed.cn/qianqiu) 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU) 网络安全何…

PyTorch深度学习实战(26)——多对象实例分割

PyTorch深度学习实战&#xff08;26&#xff09;——多对象实例分割 0. 前言1. 获取并准备数据2. 使用 Detectron2 训练实例分割模型3. 对新图像进行推断小结系列链接 0. 前言 我们已经学习了多种图像分割算法&#xff0c;在本节中&#xff0c;我们将学习如何使用 Detectron2 …

单页404源码

<!doctype html> <html> <head> <meta charset"utf-8"> <title>简约 404错误页</title><link rel"shortcut icon" href"./favicon.png"><style> import url("https://fonts.googleapis.co…

C# 字体大小的相关问题

设置字体大小无法这么写&#xff0c; button1.Font.Size 20&#xff1b; 这个是只读属性&#xff1b; 把字体大小改为16&#xff0c; button2.Font new Font(button2.Font.Name, 16); 程序运行的时候先看一下窗体和控件的默认字体尺寸&#xff0c;都是9&#xff1b;然后点b…