你真的会数据结构吗:双向链表

❀❀❀ 文章由@不准备秃的大伟原创 ❀❀❀

♪♪♪ 若有转载,请联系博主哦~ ♪♪♪

❤❤❤ 致力学好编程的宝藏博主,代码兴国!❤❤❤

        各位铁汁们,大家好啊,这里是持续不断学习的大伟。不知道大家有没有开学或者是上班了呢?反正博主是开学了,那既然开学了,咱们的心态也要从假期生活转变为学习生活了,就需要一丝不苟的汲取知识了,那么废话不多说,赶紧开始今天的学习吧! 

        上一篇文章我们提到了单向链表,什么,忘了?那还不赶紧回去复习--------->  :单链表 

此外,我们还谈到了链表的八种形态,上一篇我们实现的是单向循环不带头链表,今天我们要实现的就是带头双向循环链表,如图:40f841f79aed4673a732deb7a8a35c2d.png

        那么问题来了,有小伙伴一直都好奇的 “带头” 是什么意思,其实很简单:“头” 就是头结点,一般不存储有效数据,而是像一个哨兵一样占着头结点的位置,所以我们一般把“头”称之为“哨兵位” 。那么,开码!

        当然了,老规矩三个文件:DLT.h  , DLT.c , test.c

        DLT.c

        首先我们需要定义一个结构体,那我们需要什么样的指针呢?当然了,1.保存链表节点的变量data  2.以便找到链表节点的后面节点的next节点,这些和单链表都是一样的  3.由于是双向链表,所以我们需要一个以便找到链表前面的节点,我们命名为prev:

typedef int LTDataType;
//当然了,一如既往的重命名
typedef struct ListNode
{struct ListNode* next;struct ListNode* prev;LTDataType data;
}DLNode;

        接下来就是各种功能的声明:(双向链表和单链表的功能基本差不多,大家自己想一想哦~(ง •_•)ง)

DLNode* LTCreative();
//创建哨兵位
bool DLEmpty(DLNode* phead);
//检查链表是否为空
DLNode* LTBuyNode(LTDataType x);
//创建节点
DLNode* LTSearch(DLNode* phead, LTDataType x);
//寻找某节点
void LTPrint(DLNode* phead);
//打印链表
void LTInsert(DLNode* pos, LTDataType x);
//链表某位置插入
void LTPushFront(DLNode* phead, LTDataType x);
//链表头插
void LTPushBack(DLNode* phead, LTDataType x);
//链表尾插
void LTErase(DLNode* pos);
//链表删除某位置节点
void LTPopFront(phead);
//链表头删
void LTPopBack(DLNode* phead);
//链表尾删
void LTDestroy(DLNode* phead);
//摧毁链表

        当然,别忘了头文件的声明:

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

        DLT.c

                创建头结点

DLNode* LTCreative()
{DLNode* phead = LTBuyNode(-1);
//这里调用了创建节点,并给予哨兵位无效的数据,如-1phead->next = phead;phead->prev = phead;
//由于此时链表没有其他节点,所以哨兵位的前面后面都是自己return phead;
}

                创建节点

DLNode* LTBuyNode(LTDataType x)
{DLNode* newnode = (DLNode*)malloc(sizeof(DLNode));//向内存申请空间空间if (newnode == NULL){perror("malloc fail");return NULL;}newnode->data = x;newnode->next = NULL;newnode->prev = NULL;
//因为暂时不确定前后关系,所以全都指向空return newnode;
}

                检查链表是否为空

bool DLEmpty(DLNode* phead)
{assert(phead);return phead->next == phead;//如果哨兵位的下一位是自己(前一位也一样)
}

                插入节点

void LTInsert(DLNode* pos, LTDataType x)
{assert(pos);DLNode* newnode = LTBuyNode(x);
//创建节点DLNode* prev = pos->prev;prev->next = newnode;pos->prev = newnode;newnode->next = pos;newnode->prev = prev;
}

        这里简单给大家画了个图,虽然很丑,但是伴着走读代码应该是很好理解的,不然,你自己试试? 上面最后五行代码确实但凭看是比较难理解的,但是看着图,我们就会发现:先创建个prev来充当(保存)pos前面位置的节点,然后改变newnode与prev的前后关系,再改变pos与newnode的关系。

89eb302204784272b8a54af0d0830c31.png

        这里大家有没有意识到为什么博主是先写的某位置的插入,而不是头插或者尾插?想必聪明的你已经想到原因了:因为可以复用

        不信,接下来给你展示:

                头插

void LTPushFront(DLNode* phead, LTDataType x)
{assert(phead);LTInsert(phead->next,x);
}

                尾插

void LTPushBack(DLNode* phead, LTDataType x)
{assert(phead);LTInsert(phead, x);//哨兵位的上一个位置就是链表的尾
}

                 打印

void LTPrint(DLNode* phead)
{DLNode* cur = phead->next;
//当然,哨兵位不打印while (cur != phead){printf("%d<-->", cur->data);cur = cur->next;}
//遍历链表printf("\n");
}

                查找某节点 

DLNode* LTSearch(DLNode* phead, LTDataType x)
{assert(phead);DLNode* cur = phead->next;while (cur != phead){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}
//和打印很像,都是要遍历一遍链表

                删除某位置的节点

void LTErase(DLNode* pos)
{assert(pos);DLNode* prev = pos->prev;DLNode* next = pos->next;prev->next = next;next -> prev = prev;free(pos);
}

        同样的,博主也来一张手稿图:简单来说,就是找到pos位置的前后两个位置,然后将prev和next联系起来,这样子就直接把pos位置的节点略过了(当然,别忘了释放pos位置的空间)。 

25dabf2f07064046a79db88ade646e69.png

                同样的,为什么博主先写删除某位置,而不是头删或者尾删,大家大声喊出来那两个字:复用! 

                尾删

void LTPopBack(DLNode* phead)
{assert(phead);assert(!DLEmpty(phead));
//链表为空当然不好删除了,对吧~LTErase(phead->prev);
}

                头删

void LTPopBack(DLNode* phead)
{assert(phead);assert(!DLEmpty(phead));LTErase(phead->next);
}

                摧毁链表

void LTDestroy(DLNode* phead)
{assert(phead);DLNode* cur = phead->next;
//先将哨兵位当做循环结束的标志,再最后释放while (cur != phead){DLNode* next = cur->next;free(cur);cur = next;}free(phead);
}

        o啦,到此我们的双向链表就结束了,是不是感觉要比单链表写的爽啊?毕竟可以有四个地方复用嘛,那,玩一玩呗~:

        test.c 

test()
{DLNode* plist = LTCreative();LTPushFront(plist, 1);//1LTPrint(plist);LTPushFront(plist, 2);//2<-->1LTPrint(plist);LTPushFront(plist, 3);//3<-->2<-->1LTPrint(plist);LTPushBack(plist, 4);//3<-->2<-->1<-->4LTPrint(plist);LTPushBack(plist, 5);//3<-->2<-->1<-->4<-->5LTPrint(plist);DLNode* pos = LTSearch(plist, 2);if (pos != NULL){LTErase(pos);//3<-->1<-->4<-->5}LTPrint(plist);LTPopBack(plist);//3<-->1<-->4LTPrint(plist);LTPopFront(plist);//1<-->4LTPrint(plist);
}int main()
{test();return 0;
}

        我们来看看结果是否对的上: 

dd449e5dac0b4b248a087d3c392ddb7a.png

        可以看到,答案完全符合,所以说我们的双向链表已经写完了!啪叽啪叽(如果想测试其他功能可以私下测试哦~) 

        到这里本篇博客已经可以是完结了,但是接着链表后面就是栈和队列了,下篇文章我会对大家介绍,请大家继续支持大伟,谢谢啦~!  谢啦!!☆⌒(*^-゜)v

        There are no shortcuts to any place worth going. 到任何值得去的地方都没有捷径。

        本篇博客也就到此为止了,送大家一碗鸡汤,勉励自己以及这世界上所有追逐梦想的赤子趁年华尚好努力提升自己,莫欺少年穷!   

794607b4caaa4c11975cc4d68f5a23e9.jpeg

 

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Three.js 基础属性

三维坐标系 辅助观察坐标系 THREE.AxesHelper()的参数表示坐标系坐标轴线段尺寸大小&#xff0c;你可以根据需要改变尺寸。 // AxesHelper&#xff1a;辅助观察的坐标系 const axesHelper new THREE.AxesHelper(150); scene.add(axesHelper);材质半透明设置 设置材质半透明…

vant安装教程(基于vue3)

1、先安装 npm i vant 如果不行安装这个 yarn add vant 2、在main.js中引入即可 import { createApp } from vue import App from ./App.vue import router from ./router import store from ./store import { Button } from vant; import vant/lib/index.css;createApp(App).…

Cesium 展示——加载 tileset.json 格式的模型数据

文章目录 需求分析需求 已给 tileset.json 文件,现需加载该模型文件, 该模型特点:模型上的各模块均可以进行点击设置,且相机视角拉近后可以看到内部隐藏的物件模块 分析 tileset.json :模型数据【模型加载】方法export function init3dTileLayer (option) {var tilesetMo…

搜索专项---IDA*

文章目录 排书回转游戏 一、排书OJ链接 本题思路:先考虑每一步的决策数量&#xff1a;当抽取长度为 i 的一段时&#xff0c;有 n−i1 种抽法&#xff0c;对于每种抽法&#xff0c;有 n−i 种放法。另外&#xff0c;将某一段向前移动&#xff0c;等价于将跳过的那段向后移动&am…

有哪些副业渠道?

夸克网盘这个软件出来好久了&#xff0c;官方前不久才开通了推广渠道&#xff0c;这就给了我们以此赚钱的机会。具体时间应该是在2022年12月份。 所谓夸克网盘拉新&#xff0c;就是夸克网盘为了抢占市场&#xff0c;与其他网盘竞争对手&#xff08;百度网盘、迅雷网盘等&#…

【软件架构】01-架构的概述

1、定义 软件架构就是软件的顶层结构 RUP&#xff08;统一过程开发&#xff09;4 1 视图 1&#xff09;逻辑视图&#xff1a; 描述系统的功能、组件和它们之间的关系。它主要关注系统的静态结构&#xff0c;包括类、接口、包、模块等&#xff0c;并用于表示系统的组织结构…

从私人客户转变为教练会员网站

教练和顾问可以做出的最令人兴奋的转变之一就是通过教练会员网站扩大业务规模。 一对多优惠的类型有很多种&#xff0c;但与任何其他选择相比&#xff0c;教练和顾问的会员资格拥有最多的机会和灵活性&#xff0c;可以与你和你的客户一起发展。 世界正在转向更容易获得和更…

【程序员必备技能】Git入门

目录 &#x1f308;前言&#x1f308; &#x1f4c1; Git的概念 &#x1f4c2; 版本控制 &#x1f4c2; 集中式 和 分布式 ​ &#x1f4c1; 创建和配置本地仓库 &#x1f4c1; 理解工作区&#xff0c;暂存区&#xff0c;版本库 &#x1f4c1; Git的基本操作 &#x1f4c2;…

如何增加层次厚度?

Q 老师&#xff0c;我在做一个斧头武器&#xff0c;如何在平面上增加厚度和层次呢&#xff1f; A 选中这几个线&#xff0c;点连接就会出现中线&#xff0c;把中线稍作调整即可~

Open3D 基于最小生成树的法线定向 (27)

Open3D 基于最小生成树的法线定向 (27) 一、算法介绍二、算法实现一、算法介绍 法线计算的方向通常都存在方向问题,用Open3D估计的点云法线,是在每个点的局部进行拟合,估计的法线方向并不一致,Open3D提供了使用最小生成树调整法线到统一方向的方法,下面是具体的实现代码…

LeetCode 热题 100 | 二叉树(二)

目录 1 543. 二叉树的直径 2 102. 二叉树的层序遍历 3 108. 将有序数组转换为二叉搜索树 菜鸟做题&#xff0c;语言是 C 1 543. 二叉树的直径 这道题和 124. 二叉树中的最大路径和 太像了 题眼&#xff1a;二叉树的 直径 是指树中任意两个节点之间 最长路径的长度 。…

174基于matlab的雷达数字信号处理

基于matlab的雷达数字信号处理。该程序具备对雷达目标回波的处理能力&#xff0c;能够从噪声中将目标检测出来&#xff0c;并提取目标的距离、速度、角度信息。有相应的试验文档。程序已调通&#xff0c;可直接运行。 174 雷达数字信号处理 目标检测出来 (xiaohongshu.com)

Apache DolphinScheduler 3.2.1 版本发布:增强功能与安全性的全面升级

近期&#xff0c;Apache DolphinScheduler 社区激动地宣布 3.2.1 版本的发布。此次更新不仅着力解决了前一版本&#xff08;3.2.0&#xff09;中遗留的问题&#xff0c;而且引入了一系列的功能增强和优化措施。 原先的问题主要源于部分重要代码在发布过程中未能成功合并&#x…

多表联合分页查询(二)---- springboot整合MybatisPlus分页代码

目录 一、分页配置代码解读&#xff08;使用MP自带分页&#xff09;二、Controller层代码解读三、service层代码解读四、Mapper层代码解读五、结果展示 一、分页配置代码解读&#xff08;使用MP自带分页&#xff09; package com.minster.yanapi.Config;import com.baomidou.m…

matlab 三质量-弹簧系统受激振力

1、内容简介 略 44-可以交流、咨询、答疑 建立系统运动方程&#xff0c;研究固有频率和对应主振型 2、内容说明 略 三质量&#xff0d;弹簧系统受激振力&#xff0c;并不考虑各自的阻尼。建立系统运动方程。 解&#xff1a;由于阻尼对固有频率没有影响&#xff0c;故本文不…

Sora将创造多少算力需求?

1.1 Sora 训练与推理算力需求初步测算 Sora发布表现亮眼&#xff0c;TransformerDiffusion架构或成为文生视频大模型新范式。据Sora技术报告&#xff0c;类似于LLM将不同文本数据统一为token&#xff0c;Sora可将不同类型的视频和图像等视觉数据统一为patches&#xff0c;具体…

IDA使用-2023CICSN华中赛区pwn题逆向为例

文章目录 相关字节标识导入函数和导出函数找程序入口函数选项设置重命名CISCN2023华中赛区分区赛AWDIDA源码main 构造结构体sub_141B() 打开局部变量类型的视图增加变量类型重新定义变量类型再次设置变量类型并重新定义再次设置变量类型并重新定义再次设置变量类型并重新定义 设…

【数据结构与算法】(20)高级数据结构与算法设计之 Greedy Algorithm 贪心算法 代码示例与详细讲解

目录 4.2 Greedy Algorithm1) 贪心例子DijkstraPrimKruskal 2) 零钱兑换问题有几个解&#xff08;零钱兑换 II&#xff09;Leetcode 518最优解&#xff08;零钱兑换&#xff09;- 穷举法 Leetcode 322最优解&#xff08;零钱兑换&#xff09;- 贪心法 Leetcode 322 3) Huffman …

线程池的常用实现及执行流程

线程池 线程池线程池接口线程池参数线程池分类动态数目线程池固定数目线程池单例线程池任务调度线程池 线程池的执行流程 线程池 线程池接口 线程池参数 1、corePoolSize&#xff1a;核心线程数&#xff0c;线程池中最少线程&#xff0c;核心线程不会被回收。 2、maximumPoo…

6-pytorch-神经网络搭建

b站小土堆pytorch教程学习笔记 1.神经网络骨架搭建&#xff1a;Containers 官方文档代码&#xff1a; import torch.nn as nn import torch.nn.functional as Fclass Model(nn.Module):def __init__(self):super().__init__()self.conv1 nn.Conv2d(1, 20, 5)self.conv2 nn.…