【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.mzph.cn/news/680053.shtml

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

相关文章

STM32 + ESP8266,连接阿里云 上报/订阅数据

&#xff08;文章正在编辑中&#xff0c;一点点地截图操作过程&#xff0c;估计要拖拉两三天&#xff09; 一、烧录MQTT固件 ESP8266出厂时&#xff0c;默认是AT固件。连接阿里云&#xff0c;需要使用MQTT固件。 1、独立EPS8266模块的烧录方法 2、魔女开发板&#xff0c;板载…

‘vue-cli-service‘ 不是内部或外部命令,也不是可运行的程序

遇到 vue-cli-service 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。 的错误时&#xff0c;通常意味着Vue CLI没有被正确安装或配置在项目中。这可能是因为node_modules目录缺失了必要的包&#xff0c;或者局部安装的Vue CLI没有被正确设置到系统的PATH环境…

洛谷: P1308 [NOIP2011 普及组] 统计单词数

前言: 这道题没理解清题目表达意思&#xff0c;我开始想的是用map来记录个数&#xff0c;然后一个变量记录一开始出现的单词位置&#xff0c;不挺简单的吗&#xff0c;然后....就AC了2个..从错误提示能看到个数没啥问题&#xff0c;但是第一个单词位置不对&#xff0c;看了新样…

【51单片机】AT24C02(江科大、爱上半导体)

一、AT24C02 1.AT24C02介绍 AT24C02是一种可以实现掉电不丢失的存储器,可用于保存单片机运行时想要永久保存的数据信息 存储介质:E2PROM 通讯接口:12C总线 容量:256字节 2.引脚即应用电路 本开发板AT24C02原理图 12C地址全接地,即全为0 WE接地,没有写使能 SCL接P21 S…

Microsoft Excel 加载数据分析工具

Microsoft Excel 加载数据分析工具 1. 打开 Excel&#xff0c;文件 -> 选项2. 加载项 -> 转到…3. 分析工具库、分析工具库 - VBA4. 打开 Excel&#xff0c;数据 -> 数据分析References 1. 打开 Excel&#xff0c;文件 -> 选项 2. 加载项 -> 转到… ​​​ 3…

不安全的 HTTP请求 漏洞原理以及修复方法

漏洞名称&#xff1a;不安全的HTTP方法、危险的HTTP方法 漏洞描述&#xff1a;不安全的HTTP方法一般包括&#xff1a;TRACE、PUT、DELETE、COPY 等。其中最常见的为TRACE方法可以回显服务器收到的请求&#xff0c;主要用于测试或诊断&#xff0c;恶意攻击者可以利用该方法进行…

【Java程序设计】【C00270】基于Springboot的moba类游戏攻略分享平台(有论文)

基于Springboot的moba类游戏攻略分享平台&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的游戏攻略分享平台 本系统分为系统功能模块、管理员功能模块、以及用户后台功能模块。 系统功能模块&#xff1a;在平台首…

【开源】基于JAVA+Vue+SpringBoot的房屋出售出租系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 房屋销售模块2.2 房屋出租模块2.3 预定意向模块2.4 交易订单模块 三、系统展示四、核心代码4.1 查询房屋求租单4.2 查询卖家的房屋求购单4.3 出租意向预定4.4 出租单支付4.5 查询买家房屋销售交易单 五、免责说明 一、摘…

Ocr之TesseractOcr的安装及使用

目录 一、安装环境 二、安装内容 三、安装过程及识别测试 1. 安装过程 2. 程序编写 总结 1. 安装复杂度较低 2. 国外开源Ocr 3. 可设置识别参数 4. 工具类 一、 系统环境windows 10 linux环境也可安装, 可借鉴此篇文章>> | 二、安装内容 Tesseract exe 程序安…

python学习23

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…

数模.matlab符号计算方程

一、符号函数 a&#xff1a;整理简化&#xff1a; b&#xff1a;因式分解&#xff1a; c&#xff1a;多项式展开 d&#xff1a;合并&#xff1a; e&#xff1a;计算分子分母&#xff1a; f&#xff1a;求导&#xff1a; f&#xff1a;差分&#xff1a; g&#xff1a;不定积分&a…

阿里云服务器“带宽计费模式”怎么选?有啥区别?

阿里云服务器带宽计费模式分为“按固定带宽”和“按使用流量”&#xff0c;有什么区别&#xff1f;按固定带宽是指直接购买多少M带宽&#xff0c;比如1M、5M、10M、100M等&#xff0c;阿里云直接分配用户所购买的带宽值&#xff0c;根据带宽大小先付费再使用&#xff1b;按使用…

BYTEVALUE 百为流控路由器远程命令执行漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

单片机接收PC发出的数据

#include<reg51.h> //包含单片机寄存器的头文件 /***************************************************** 函数功能&#xff1a;接收一个字节数据 ***************************************************/ unsigned char Receive(void) { unsigned char dat; …

网络编程项目:电子辞典

项目要求&#xff1a; 登录注册功能&#xff0c;不能重复登录&#xff0c;重复注册。用户信息也存储在数据库中。单词查询功能历史记录功能&#xff0c;存储单词&#xff0c;意思&#xff0c;以及查询时间&#xff0c;存储在数据库基于TCP&#xff0c;支持多客户端连接&#x…

C++引用(内含和指针的对比)

1.引用的概念 概念&#xff1a;引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和它引用的变量共用一块内存空间。 类型& 引用变量名(对象名) 引用实体&#xff1b;&#xff08;&这个符…

leetcode 394. 字符串解码

本题主要思路是使用两个栈&#xff1a;一个用于存储倍数&#xff08;数字&#xff09;&#xff0c;另一个用于存储解码结果&#xff08;字符串&#xff09;。在遍历输入字符串时&#xff0c;根据不同的字符执行相应的操作&#xff0c;最终得到解码后的字符串。 具体来说&#x…

从零开始实现消息队列(二)

从零开始实现消息队列 .核心API交换机类型持久化网络通信Connection和Channel 消息应答模块划分 . 核心API 对于Broker来说,要实现以下核心API,通过这些API来实现消息队列的基本功能. 创建队列(queueDeclare)销毁队列(queueDelete)创建交换机(exchangeDeclare)销毁交换机(exc…

Duilib List 控件学习

这是自带的一个示例; 一开始运行的时候List中是空的,点击Search按钮以后就填充列表框; 先看一下列表框列头是在xml文件中形成的; <List name="domainlist" bkcolor="#FFFFFFFF" ... menu="true"> <ListHeader height="24…