【初阶数据结构】双向链表

在这里插入图片描述

文章目录

  • 双向链表
    • 1.申请节点
    • 2.链表初始化
    • 3.尾插
    • 4.打印链表
    • 5.头插
    • 6.尾删
    • 7.头删
    • 8.查找
    • 9.指定位置插入
    • 10.删除pos节点
    • 11.链表的销毁
    • 12.程序源码

双向链表

链表分类 8种 (带头/不带头 单向/双向 循环/循环)
最常用两种 单链表(不带头单向不循环链表) 双向链表(带头双向循环链表)

双链表有 头节点(哨兵位) 后继指针(next) 前驱指针(prev)

双向链表为空时,为一个哨兵位

同样采用多文件操作

在这里插入图片描述

1.申请节点

//申请节点
LTNode* LTBuyNode(LTDataType x)
{LTNode* node = (LTNode*)malloc(sizeof(LTNode));if (node == NULL)//申请不成功{perror("malloc fail");exit(1);//给非0值}node->data = x;node->next = node->prev = node;return node;
}

2.链表初始化

void LTInit(LTNode** pphead)
{//给双向链表创建一个哨兵位*pphead = LTBuyNode(-1);//随便赋一个值
}

3.尾插

在这里插入图片描述

以上图为例 尾插时 head->prev指向的就是d3

void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);//phead phead->prev newnodenewnode->prev = phead->prev;//也就是newnode->prev指向d3newnode->next = phead;phead->prev->next = newnode;//d3的next指针指向新节点phead->prev = newnode;
}

4.打印链表

在这里插入图片描述

让pcur指向phead的next指针,然后遍历链表,直到pcur=phead

void LTPrint(LTNode* phead)
{LTNode* pcur = phead->next;while (pcur != phead){printf("%d->", pcur->data);pcur = pcur->next;}printf("\n");
}

5.头插

头插是插到头结点与d1之间,插在头结点前边跟尾插一样(因为链表是双向循环的)

在这里插入图片描述

void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);newnode->next = phead->next;newnode->prev = phead;phead->next->prev = newnode;//先改影响小的phead->next = newnode;}

6.尾删

在这里插入图片描述

void LTPopBack(LTNode* phead)
{//链表必须有效且链表不能为空assert(phead && phead->next != phead);LTNode* del = phead->prev;del->prev->next = phead;phead->prev = del->prev;//删除del节点free(del);del = NULL;}

7.头删

在这里插入图片描述

void LTPopFront(LTNode* phead)
{assert(phead && phead->next != phead);LTNode* del = phead->next;phead->next = del->next;del->next->prev = phead;//删除del节点free(del);del = NULL;
}

8.查找

LTNode* LTFind(LTNode* phead, LTDataType x)
{LTNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}//没有找到return NULL;
}

9.指定位置插入

在这里插入图片描述

10.删除pos节点

在这里插入图片描述

void LTErase(LTNode* pos)//这里传一级指针是为了保持接口的一致性 其他函数传的都是一级指针
{//pos理论上来说不能为phead,但是参数没有phead,所以无法增加校验pos->next->prev = pos->prev;pos->prev->next = pos->next;free(pos);pos = NULL;
}

11.链表的销毁

在这里插入图片描述

void LTDesTroy(LTNode* phead)
{assert(phead);LTNode* pcur = phead->next;while (pcur->next != phead){LTNode* next = pcur->next;free(pcur);pcur = next;}//此时pcur指向phead,而phead还没有被销毁free(phead);phead = NULL;//如果不置为空 plist(保存的地址空间被释放掉了) 若后续不再使用plist则没问题(野指针)}

12.程序源码

List.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int LTDataType;
//定义双向链表节点的结构
typedef struct ListNode
{LTDataType data;struct ListNode* next;struct ListNode* prev;}LTNode;//声明双向链表种提供的方法//初始化
void LTInit(LTNode** pphead);
void LTPrint(LTNode* phead);
//插入数据之前,链表必须初始化到只有一个头结点的情况
// 不改变哨兵位的地址,所以传一级指针即可
//尾插
void LTPushBack(LTNode* phead,LTDataType x);
//头插
void LTPushFront(LTNode* phead, LTDataType x);void LTPopBack(LTNode* phead);
void LTPopFront(LTNode* phead);
//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x);
void LTErase(LTNode* pos);
//查找
LTNode* LTFind(LTNode* phead, LTDataType x);void LTDesTroy(LTNode* phead);

List.c

#include"List.h";void LTPrint(LTNode* phead)
{LTNode* pcur = phead->next;while (pcur != phead){printf("%d->", pcur->data);pcur = pcur->next;}printf("\n");
}//申请节点
LTNode* LTBuyNode(LTDataType x)
{LTNode* node = (LTNode*)malloc(sizeof(LTNode));if (node == NULL){perror("malloc fail");exit(1);//给非0值}node->data = x;node->next = node->prev = node;return node;
}
void LTInit(LTNode** pphead)
{//给双向链表创建一个哨兵位*pphead = LTBuyNode(-1);
}
//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);//phead phead->prev newnodenewnode->prev = phead->prev;newnode->next = phead;phead->prev->next = newnode;//不能交换位置phead->prev = newnode;
}
//头插
void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);newnode->next = phead->next;newnode->prev = phead;phead->next->prev = newnode;//先改影响小的phead->next = newnode;}
//尾删
void LTPopBack(LTNode* phead)
{//链表必须有效且链表不能为空assert(phead && phead->next != phead);LTNode* del = phead->prev;del->prev->next = phead;phead->prev = del->prev;//删除del节点free(del);del = NULL;}
//头删
void LTPopFront(LTNode* phead)
{assert(phead && phead->next != phead);LTNode* del = phead->next;phead->next = del->next;del->next->prev = phead;//删除del节点free(del);del = NULL;
}
//查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{LTNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}//没有找到return NULL;
}
//指定位置插入数据
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newnode = LTBuyNode(x);newnode->next = pos->next;newnode->prev = pos;pos->next->prev = newnode;pos->next = newnode;}
//删除pos节点
void LTErase(LTNode* pos)//这里传一级指针是为了保持接口的一致性 其他函数传的都是一级指针
{//pos理论上来说不能为phead,但是参数没有phead,所以无法增加校验pos->next->prev = pos->prev;pos->prev->next = pos->next;free(pos);pos = NULL;
}
//链表销毁
void LTDesTroy(LTNode* phead)
{assert(phead);LTNode* pcur = phead->next;while (pcur->next != phead){LTNode* next = pcur->next;free(pcur);pcur = next;}//此时pcur指向phead,而phead还没有被销毁free(phead);phead = NULL;//如果不置为空 plist(保存的地址空间被释放掉了) 若后续不再使用plist则没问题(野指针)}
//LTErase和LTDestroy参数理论上要传二级,因为我们需要让形参的改变影响到实参,但是为了接口的一致性才穿的一级
//传一级存在的问题是,当形参phead置为NULL后,实参plist不会被修改为NULL,因此解决方法是 调用完方法后手动将实参置为NULL

test.c

#define _CRT_SECURE_NO_WARNINGS
#include"List.h";void ListTest01()
{LTNode* plist = NULL;LTInit(&plist);LTPushBack(plist, 1);LTPrint(plist);LTPushFront(plist, 2);LTPrint(plist);LTPopBack(plist);LTPrint(plist);LTPopFront(plist);LTPushFront(plist, 3);LTPrint(plist);LTNode* find = LTFind(plist, 3);{if (find == NULL){printf("找不到\n");}else{printf("找到了\n");}}LTInsert(find, 66);LTPrint(plist);LTErase(find);LTPrint(plist);}
int main()
{ListTest01();return 0;
}

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

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

相关文章

从 Prompt 管理到人格稳定:探索 Cursor AI 编辑器如何赋能 Prompt 工程与人格风格设计(下)

六、引入 Cursor AI 编辑器的开发流程革新 在整个系统开发过程中&#xff0c;我大量采用了 Cursor 编辑器作为主要的开发环境&#xff0c;并获得以下关键收益&#xff1a; 具备 AI 补全与代码联想功能&#xff1a;支持通过内置 Copilot 模型对 Python、FastAPI、YAML、JSON 等…

Spark运行架构

Spark框架的核心是一个计算引擎&#xff0c;整体来说&#xff0c;它采用了标准master-slave的结构  如下图所示&#xff0c;它展示了一个Spark执行时的基本结构&#xff0c;图形中的Driver表示master&#xff0c;负责管理整个集群中的作业任务调度&#xff0c;图形中的Executo…

基于未合入PR创建增量patch的git管理方法

目录前言准备操作步骤精准移植基础PR到本地分支修改代码鸿蒙编译、调试、测试具体编译指令、测试步骤这里带过&#xff0c;这不是本文论述重点创建diff文件工作仓库应用最新patch总结前言 作为程序员&#xff0c;多人协同开发同一个需求是正常的。即使是自己一个人搞需求&…

git真正更新项目

背景 Fetch all remote后flutter代码都拉下来&#xff0c;都是Android项目应用不上&#xff1b;git–>update project才生效&#xff01;&#xff01;&#xff01;

AI时代如何拓展Web前端开发的边界

文章目录 1 从“页面仔”到“智能体验构建者”——前端的变与不变2 AI 如何重塑 Web 前端&#xff1a;从开发到用户体验的革命2.1 AI 赋能开发效率&#xff1a;前端工程师的“超级外挂”2.1.1 智能代码辅助与生成2.1.2 自动化测试与 Bug 定位 2.2 AI 提升用户体验&#xff0c;构…

chrome webdrive异常处理-session not created falled opening key——仙盟创梦IDE

注册表错误 :EKKOK:chromeinstallerut1 Lgoogle update settings.cc:26b falled opening key .( e\Update\ClientStateMedium 8A69D345-D564-463c-AFF1-A69D9E530F96} to set usagestats 连接超时 disconnected: received Inspector.detached eventfailed to check if windo…

【Java EE初阶 --- 多线程(进阶)】JUC

乐观学习&#xff0c;乐观生活&#xff0c;才能不断前进啊&#xff01;&#xff01;&#xff01; 我的主页&#xff1a;optimistic_chen 我的专栏&#xff1a;c语言 &#xff0c;Java 欢迎大家访问~ 创作不易&#xff0c;大佬们点赞鼓励下吧~ 文章目录 JUC组件ReentrantLock与s…

免费静态网站搭建

免费静态网站搭建 内容简介搭建步骤GitHub仓库创建Jekyll安装使用Jekyll安装指南Jekyll快速搭建测试Jekyll后续玩法 内容简介 &#x1f6a9;Tech Contents&#xff1a;GithubPage/Jekyll/Custom URLs &#x1f431;GitHub Pages&#xff1a;静态网站托管服务&#xff0c;自动将…

MySQL 8.0 OCP 1Z0-908 题目解析(21)

题目81 Choose two. Examine the modified output: mysql> SHOW SLAVE STATUS\G *************************** 1. row ***************************Slave_IO_Running: YesSlave_SQL_Running: YesSeconds_Behind_Master: 1612Seconds_Behind_Master value is steadily gro…

Web前端开发-HTML、CSS

文章目录是什么&#xff1f;HTML快速入门VS Code开发工具基础标签&样式新浪新闻-标题标题排版标题样式标题样式-1标题样式-2超链接新浪新闻-正文新浪新闻-正文排版新浪新闻-页面布局表格标签表单标签表单标签-表单项是什么&#xff1f; HTML快速入门 VS Code开发工具 基础标…

Vue.js状态管理: Vuex在大型项目中的实际应用

# Vue.js状态管理: Vuex在大型项目中的实际应用 ## 一、Vuex核心架构与大型项目适配 ### 1.1 状态管理&#xff08;State Management&#xff09;的本质需求 在复杂前端系统中&#xff0c;组件间的数据传递成本随项目规模呈指数级增长。根据Vue官方统计&#xff0c;超过500个组…

C++开发:结构体作为函数形参的值传递与引用传递

笔者定义了一个结构体变量&#xff0c;用于作为函数的形参&#xff0c;定义如下&#xff1a;struct CardParameters {float* Average nullptr;int averageSize 0; }; 需求描述&#xff1a;结构体变量作为函数的形参&#xff0c;在函数体中给指针变量分配内存空间并赋值&#…

【unity小技巧】在 Unity 中将 2D 精灵添加到 3D 游戏中,并实现阴影投射效果,实现类《八分旅人》《饥荒》等等的2.5D游戏效果

注意&#xff1a;考虑到unity小技巧的内容比较多&#xff0c;我将该内容分开&#xff0c;并全部整合放在【unity小技巧】专栏里&#xff0c;感兴趣的小伙伴可以前往逐一查看学习。 文章目录 前言实战1、在3D场景中&#xff0c;新建一些不同形状的2D图片2、我们新建一个Lit材质3…

Rust 内存结构:深入解析

Rust 的内存管理系统是其核心特性之一&#xff0c;结合了手动内存管理的效率与自动内存管理的安全性。以下是 Rust 内存结构的全面解析&#xff1a; 内存布局概览 ----------------------- | 代码段 (Text) | 只读&#xff0c;存储可执行指令 ----------------------…

【Chrome】‘Good助手‘ 扩展程序使用介绍

这是我开发的一款 Chrome 浏览器扩展程序&#xff0c;目前主要集成了‘AI对话‘&#xff0c;’总结页面’&#xff0c;‘基于页面问答’等功能&#xff0c;最近几天我也将写一篇介绍如何开发 chrome 扩展程序的博客&#xff0c;带你了解如何开发属于自己的插件。 注&#xff1…

基于mysql8.0.27部署1主2从的MHA集群

目录 一、mysql概述 1.1、关系型数据库 1.2、MySQL数据库 1.3、RDBMS术语 二、mysql的部署 2.1、拉取mysql 2.2、解压 2.3、 改名 2.4、 指定安装文件位置 2.5、 创建用户组 2.6、 修改mysql配置文件 2.7、创建data文件夹 2.8、更改mysql目录权限 2.9、初始化数据…

Highcharts 安装使用教程

一、Highcharts 简介 Highcharts 是一款使用 JavaScript 编写的前端数据可视化库&#xff0c;支持折线图、柱状图、饼图、面积图、散点图等多种图表类型&#xff0c;特点是渲染性能优秀、交互丰富、兼容性强&#xff0c;适合构建商业图表、统计报表等。 二、Highcharts 安装方…

Qt中的坐标系

Qt中的坐标系 1.坐标系概念2.数学坐标系VS计算机坐标系3.Qt坐标系4.像素 &#x1f31f;&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f;&#x1f31f; &#x1f680;&#x1f680;系列专栏&#xff1a;【Qt的学习】 &#x1f4dd;&#x1f4dd;本篇内容&am…

C++原子类型操作与内存序

C原子类型操作与内存序详解 这段内容深入介绍了C标准原子类型的操作接口、内存序语义及使用规范。以下是关键知识点的分层解析&#xff1a; 一、原子类型的命名规则与类型映射 C提供两种方式表示原子类型&#xff1a; 模板特化形式&#xff1a;std::atomic<T>别名形式…

互联网摸鱼日报(2025-07-07)

互联网摸鱼日报(2025-07-07) 钛媒体 一场突如其来的“召回潮”&#xff0c;点燃中国制造的“灵魂拷问” 史上最大外卖补贴战开打&#xff0c;美团聚拢资源迎战“巨无霸” 1315亿加冕潮汕女首富&#xff0c;“最强打工妹”剑指港股 用14346字&#xff0c;讲透上市前必做的10…