当前位置: 首页 > news >正文

双向带头循环链表

一、概念

何为双向:此链表每一个节点的指针域由两部分组成,一个指针指向下一个节点,另一个指针指向上一个节点,并且两头的节点也是如此,头节点的下一个节点是尾节点,尾节点的上一个节点是头节点;

何为带头:此处的头节点是一个 哨兵位,在链表定义时就要手动设置,此节点只是起到头的作用,并不是真正的节点;在双向带头循环链表为空时,链表中只有一个头节点,当链表中连头节点都不存在时,此链表不能被称作一个有效链表;

何为循环:头节点的指针域中能指向前一个节点的指针指向尾节点,尾节点中能指向下一个节点的指针指向头节点,使得这个链表循环;


二、节点结构

struct ListNode
{ListDatatype data;struct ListNode* next;struct ListNode* prev;
};

data存储节点的胡数据,next作用是指向下一个节点,prev的作用是指向前一个节点;


三、基本功能实现

1、头文件

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<assert.h>
typedef int ListDatatype;typedef struct ListNode
{ListDatatype data;struct ListNode* next;struct ListNode* prev;
}ListNode;//初始化链表
void LTInit(ListNode** pphead);//尾插
void LTPushBack(ListNode* phead, ListDatatype x);//头插
void LTPushFront(ListNode* phead, ListDatatype x);//头删
void LTPopFront(ListNode* phead);//尾删
void LTPopBack(ListNode* phead);//查找节点
ListNode* NodeFind(ListNode* phead,ListDatatype x);//删除指定位置的节点
void LTErase(ListNode* phead,ListNode* pos);//在指定位置之后插入节点
void LTInsert(ListNode* phead, ListNode* pos,ListDatatype x);//销毁双向带头循环链表
void LTDestroy(ListNode** phead);

2、函数的实现

初始化

//初始化
void LTInit(ListNode** pphead)
{assert(pphead);*pphead = (ListNode*)malloc(sizeof(ListNode));if (*pphead == NULL){perror("malloc failed");exit(1);}(*pphead)->data = -1;(*pphead)->next = (*pphead)->prev = *pphead;
}

初始化传二级指针,涉及到头节点的修改,默认-1为无效值,给定头节点的数据域是-1;让头节点的next和prev指针都指向自己,实现循环。

申请节点

//申请节点
ListNode* BuyNode(ListDatatype x)
{ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL){perror("malloc failed");exit(1);}newnode->data = x;newnode->next = newnode;newnode->prev = newnode;
}

申请的节点数据域为你传进的值;同样地,新节点地next和prev指针都指向自己;

尾插

//尾插
void LTPushBack(ListNode* phead, ListDatatype x)
{assert(phead);ListNode* newnode = BuyNode(x);newnode->next = phead;newnode->prev = phead->prev;phead->prev->next = newnode;phead->prev = newnode;
}

尾插不涉及到头节点地改变,所以只需要传入一级指针;尾插进入一个数据,涉及到头节点的prev指针指向发生变化,和原连边尾节点的next指针指向发生变化,为了不造成节点找不到的情况,先修改newnode的指针指向,让newnode的next指向phead,prev指向原链表的尾节点,也就是phead->prev;此操作完成后,先让原链表的尾节点(phead->prev)的next指向newnode,使得newnode成为新的尾节点,再让头节点phead的prev指向作为新的尾节点的newnode,完成循环;

头插

//头插
void LTPushFront(ListNode* phead, ListDatatype x)
{assert(phead);ListNode* newnode = BuyNode(x);newnode->next = phead->next;newnode->prev = phead;phead->next->prev = newnode;phead->next = newnode;
}

头插是往头节点(哨兵位)的下一个指针插入节点,因为头节点不存放有效数据,在链表中的的作用只是当做一个桩子;所以先让newnode的next指针指向原链表头指针的下一个节点,也就是phead->next,再让newnode的prev指针指向phead;   再让原链表头节点之后的那个节点(phead->next)的prev指向newnode,最后再让头节点的next指针指向newnode

注意:最后两行代码不能颠倒顺序,若是颠倒,那么phead的next指针就先指向newnode,再进行(    phead->next->prev = newnode;)操作时相当于newnode->prev=newnode,这样无法实现双向(尾插同样如此)

尾删

//尾删
void LTPopBack(ListNode* phead)
{assert(phead && phead->next!=phead);ListNode* del = phead->prev;phead->prev = del->prev;del->prev->next = phead;free(del);del = NULL;
}

尾删首先要保证链表中含有可以删除的有效节点,所以assert断言中(phead->next!=phead)操作必不可少,若是只含有头节点,那么相当于phead->next=phead这行代码,那么断言错误;当然头节点是必须存在的;

先把要删除的尾节点存在del中,先不删除del,先把要修改的指针指向修改好之后再删除要删除的尾节点,这样是为了避免找不到尾节点的前后节点;pehad->prev就是尾节点,把头节点的prev指针指向倒数第二个节点也就是(del->prev),再让倒数第二个节点的next指针指向头节点;最后释放尾节点del;

头删

//头删
void LTPopFront(ListNode* phead)
{assert(phead && phead->next!=phead);//头删时要存在有效节点ListNode* del = phead->next;phead->next = del->next;del->next->prev = phead;free(del);del = NULL;
}

头删删除的是头节点后面的那个指针,和尾删断言一样,链表中要存在有效节点,否则断言报错;先把要删除的节点存放起来,再先修改指针的指向,最后删除del;

查找结点

//查找节点
ListNode* NodeFind(ListNode* phead,ListDatatype x)
{assert(phead);ListNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}

查找节点的方法是把链表遍历一遍,看有没有数据域等于要查找的节点的数据域,若是有则返回这个节点,反之返回NULL;

在指定位置之后插入节点

//在指定位置之后插入节点
void LTInsert(ListNode* phead, ListNode* pos,ListDatatype x)
{assert(phead && pos);ListNode* newnode = BuyNode(x);newnode->next = pos->next;newnode->prev = pos;pos->next->prev = newnode;pos->next = newnode;
}

需要配合查找节点一起使用,将查找到的节点作为pos,在此后插入节点,同样地先改变newnode的先后指针指向,再去修改pos以及pos后一个几点的指针指向;

删除指定位置的节点

//删除指定位置的节点
void LTErase(ListNode* phead, ListNode* pos)
{assert(phead);assert(pos);pos->prev->next = pos->next;pos->next->prev = pos->prev;free(pos);pos = NULL;
}

pos必须存在,删除先改变删除的节点的前后节的指针的指向,再删除pos;

销毁链表

//销毁双向带头循环链表
void LTDestroy(ListNode** pphead)
{assert(pphead && *pphead);ListNode* pcur = (*pphead)->next;while (pcur != *pphead){ListNode* next = pcur->next;free(pcur);pcur = next;}free(*pphead);*pphead = NULL;
}

销毁链表涉及到头节点的改变。要传入二级指针;并且要销毁链表中每一个节点,遍历删除,最后free掉头节点并置为空。

相关文章:

双向带头循环链表

一、概念 何为双向&#xff1a;此链表每一个节点的指针域由两部分组成&#xff0c;一个指针指向下一个节点&#xff0c;另一个指针指向上一个节点&#xff0c;并且两头的节点也是如此&#xff0c;头节点的下一个节点是尾节点&#xff0c;尾节点的上一个节点是头节点&#xff1b…...

探索TASKCTL和 DataStage 的ETL任务调度协同

在复杂多变的企业环境中&#xff0c;高效、准确的数据处理是支撑业务决策与运营的核心。本文将深入探讨任务调度平台TASKCTL与ETL工具DataStage的深度融合&#xff0c;通过详尽的代码示例、结合细节以及实际案例的具体描述&#xff0c;展示这两个工具如何携手打造企业数据处理生…...

Facebook软体机器人与机器人框架:创新社交互动的未来

随着人工智能技术的不断进步&#xff0c;Facebook正通过软体机器人和先进的机器人框架&#xff0c;重新定义社交互动的未来。这些创新不仅提升了用户体验&#xff0c;也为开发者提供了强大的工具来构建下一代社交应用。 一、Facebook软体机器人&#xff1a;智能化的社交伙伴 …...

掌握音视频转换的艺术:用FFmpeg解锁多媒体的无限可能

在数字时代&#xff0c;音视频内容无处不在&#xff0c;从在线课程、娱乐视频到专业会议&#xff0c;它们都是信息传播的关键载体。然而&#xff0c;随着多媒体格式的不断演进&#xff0c;我们常常会遇到格式不兼容的问题&#xff0c;这成为了享受或处理这些内容的一大障碍。幸…...

C基础day9

一、思维导图 二、课后练习 1> 使用递归实现 求 n 的 k 次方 #include<myhead.h>int Pow(int n,int k) {if(k 0 ) //递归出口{return 1;}else{return n*Pow(n,k-1); //递归主体} }int main(int argc, const char *argv[]) {int n0,k0;printf("请输入n和k:&…...

32. 小批量梯度下降法(Mini-batch Gradient Descent)

在深度学习模型的训练过程中&#xff0c;梯度下降法是最常用的优化算法之一。我们前面介绍了批量梯度下降法&#xff08;Batch Gradient Descent&#xff09;和随机梯度下降法&#xff08;Stochastic Gradient Descent&#xff09;&#xff0c;两者各有优缺点。为了在计算速度和…...

MySQL第八次作业

一、备份与恢复作业&#xff1a; 创库,建表&#xff1a; CREATE DATABASE booksDB; use booksDB; CREATE TABLE books ( bk_id INT NOT NULL PRIMARY KEY, bk_title VARCHAR(50) NOT NULL, copyright YEAR NOT NULL ); CREATE TABLE authors …...

【合集】临时邮箱网站 临时邮箱API(持续更新)

众所周知&#xff0c;在注册一些账户时&#xff0c;比较常见的验证方式就是邮箱&#xff0c;但是在进行一些小众和不知名网站注册时&#xff0c;邮箱的泄露可能预示着不休止的邮件推送。尤其是当我们只是想临时使用邮箱这种情况&#xff0c;第二种&#xff0c;批量注册账号的情…...

职场新人感受

互联网职场感受 阶段介绍 24届6月底毕业生&#xff0c;之前从未实习过。 岗位是后端开发&#xff08;JAVA&#xff09;&#xff0c;目前已经上班三周&#xff08;前两周看文档和做了半个简单需求&#xff0c;第三周脱产新人培训&#xff09;。 职场体验 职场和想象中的工作…...

Window 下Mamba 环境安装踩坑问题汇总及解决方法 (无需绕过selective_scan_cuda)

导航 Mamba 及 Vim 安装问题参看本人之前博客&#xff1a;Mamba 环境安装踩坑问题汇总及解决方法Linux 下Vmamba 安装教程参看本人之前博客&#xff1a;Vmamba 安装教程&#xff08;无需更改base环境中的cuda版本&#xff09;Windows 下 VMamba的安装参看本人之前博客&#xf…...

前端项目本地的node_modules直接上传到服务器上无法直接使用(node-sasa模块报错)

跑 jekins任务的服务器不能连接外网下载依赖包&#xff0c;就将本地下载的 node_modules直接上传到服务器上&#xff0c;但是运行时node-sass模块报错了ERROR in Missing binding /root/component/node_modules/node-sass/vendor/linux-x64-48/binding.node >> 报错信息类…...

Hadoop3:动态扩容之新增一台机器的初始化工作

一、需求描述 给Hadoop集群动态扩容一个节点 那么&#xff0c;这个节点是全新的&#xff0c;我们需要做哪些准备工作&#xff0c;才能将它融入集群了&#xff1f; 二、初始化配置 1、修改IP和hostname vim /etc/sysconfig/network-scripts/ifcfg-ens33 vim /etc/hostname2、…...

【正点原子i.MX93开发板试用连载体验】录音小程序采集语料

本文最早发表于电子发烧友论坛&#xff1a;【新提醒】【正点原子i.MX93开发板试用连载体验】基于深度学习的语音本地控制 - 正点原子学习小组 - 电子技术论坛 - 广受欢迎的专业电子论坛! (elecfans.com) 接下来就是要尝试训练中文提示词。首先要进行语料采集&#xff0c;这是一…...

【EasyExcel】动态替换表头内容并应用样式

1.定义实体类 import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ContentStyle; import com.alibaba.excel.metadata.BorderStyleEnum; import com.alibaba.excel.metadata.VerticalAlignmentEnum; import com.alibaba.excel.metadata.…...

RocketMQ实现分布式事务

RocketMQ的分布式事务消息功能&#xff0c;在普通消息基础上&#xff0c;支持二阶段的提交。将二阶段提交和本地事务绑定&#xff0c;实现全局提交结果的一致性。 1、生产者将消息发送至RocketMQ服务端。 2、RocketMQ服务端将消息持久化成功之后&#xff0c;向生产者返回Ack确…...

【Rust练习】2.数值类型

练习题来自https://practice-zh.course.rs/basic-types/numbers.html 1 // 移除某个部分让代码工作 fn main() {let x: i32 5;let mut y: u32 5;y x;let z 10; // 这里 z 的类型是? }y的类型不对&#xff0c;另外&#xff0c;数字的默认类型是i32 fn main() {let x: i…...

通过 PPPOE 将 linux 服务器作为本地局域网 IPv4 外网网关

将 linux 服务器作为本地外网网关&#xff0c;方便利用 Linux 生态中的各种网络工具&#xff0c;对流量进行自定义、精细化管理… 环境说明 拨号主机&#xff1a;CentOS 7.9, Linux Kernel 5.4.257 拨号软件: rp-pppoe-3.11-7.el7.x86_64初始化 1、升级系统到新的稳定内核&a…...

gin源码分析

一、高性能 使用sync.pool解决频繁创建的context对象&#xff0c;在百万并发的场景下能大大提供访问性能和减少GC // ServeHTTP conforms to the http.Handler interface. // 每次的http请求都会从sync.pool中获取context&#xff0c;用完之后归还到pool中 func (engine *Engin…...

数学建模入门

目录 文章目录 前言 一、数学建模是什么&#xff1f; 1、官方概念&#xff1a; 2、具体过程 3、适合哪一类人参加&#xff1f; 4、需要有哪些学科基础呢&#xff1f; 二、怎样准备数学建模&#xff08;必备‘硬件’&#xff09; 1.组队 2.资料搜索 3.常用算法总结 4.论文撰写的…...

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(十二)-无人机群在物流中的应用

引言 本文是3GPP TR 22.829 V17.1.0技术报告&#xff0c;专注于无人机&#xff08;UAV&#xff09;在3GPP系统中的增强支持。文章提出了多个无人机应用场景&#xff0c;分析了相应的能力要求&#xff0c;并建议了新的服务级别要求和关键性能指标&#xff08;KPIs&#xff09;。…...

同三维T80006EH2-4K30编码器视频使用操作说明书:高清HDMI编码器,高清SDI编码器,4K超清HDMI编码器,双路4K超高清编码器

同三维T80006EH2-4K30编码器视频使用操作说明书&#xff1a;高清HDMI编码器&#xff0c;高清SDI编码器&#xff0c;4K超清HDMI编码器&#xff0c;双路4K超高清编码器 T80006EH2-4K30编码器 同三维&#xff0c;十多年老品牌&#xff0c;我们一直专注&#xff1a;视频采集卡、视频…...

DHCP原理及配置

目录 一、DHCP原理 DHCP介绍 DHCP工作原理 DHCP分配方式 工作原理 DHCP重新登录 DHCP优点 二、DHCP配置 一、DHCP原理 1 DHCP介绍 大家都知道&#xff0c;现在出门很多地方基本上都有WIFI&#xff0c;那么有没有想过这样一个问题&#xff0c;平时在家里都是“固定”的…...

异步日志:性能优化的金钥匙

一、背景 2024 年 4 月的一个宁静的夜晚&#xff0c;正当大家忙完一天的工作准备休息时&#xff0c;应急群里“咚咚咚”开始报警&#xff0c;提示我们余利宝业务的赎回接口成功率下降。 通过 Monitor 监控发现&#xff0c;该接口的耗时已经超过了网关配置的超时阈值(2s)&#…...

matlab仿真 模拟调制(上)

&#xff08;内容源自详解MATLAB&#xff0f;SIMULINK 通信系统建模与仿真 刘学勇编著第五章内容&#xff0c;有兴趣的读者请阅读原书&#xff09; 1.幅度调制 clear all ts0.0025; %信号抽样时间间隔 t0:ts:10-ts;%时间矢量 fs1/ts;%抽样频率 dffs/length(t); %fft的频率分…...

【数据结构】--- 堆的应用

​ 个人主页&#xff1a;星纭-CSDN博客 系列文章专栏 :数据结构 踏上取经路&#xff0c;比抵达灵山更重要&#xff01;一起努力一起进步&#xff01; 一.堆排序 在前一个文章的学习中&#xff0c;我们使用数组的物理结构构造出了逻辑结构上的堆。那么堆到底有什么用呢&…...

0基础学会在亚马逊云科技AWS上利用SageMaker、PEFT和LoRA高效微调AI大语言模型(含具体教程和代码)

项目简介&#xff1a; 小李哥今天将继续介绍亚马逊云科技AWS云计算平台上的前沿前沿AI技术解决方案&#xff0c;帮助大家快速了解国际上最热门的云计算平台亚马逊云科技AWS上的AI软甲开发最佳实践&#xff0c;并应用到自己的日常工作里。本次介绍的是如何在Amazon SageMaker上…...

护网HW面试——redis利用方式即复现

参考&#xff1a;https://xz.aliyun.com/t/13071 面试中经常会问到ssrf的打法&#xff0c;讲到ssrf那么就会讲到配合打内网的redis&#xff0c;本篇就介绍redis的打法。 未授权 原理&#xff1a; Redis默认情况下&#xff0c;会绑定在0.0.0.0:6379&#xff0c;如果没有采用相关…...

C++ //练习 15.8 给出静态类型和动态类型的定义。

C Primer&#xff08;第5版&#xff09; 练习 15.8 练习 15.8 给出静态类型和动态类型的定义。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 解释 静态类型&#xff1a;在编译时已知&#xff0c;是在变量声明时的类型或表达式生成的…...

阿里云ECS服务器安装jdk并运行jar包,访问成功详解

安装 OpenJDK 8 使用 yum 包管理器安装 OpenJDK 8 sudo yum install -y java-1.8.0-openjdk-devel 验证安装 安装完成后&#xff0c;验证 JDK 是否安装成功&#xff1a; java -version设置 JAVA_HOME 环境变量&#xff1a; 为了确保系统中的其他应用程序可以找到 JDK&…...

Windows系统上使用npm来安装和配置Yarn,在VSCode中使用

一、安装Yarn 1. 安装Node.js和npm 如果还没有安装Node.js和npm&#xff0c;可以从Node.js官方网站下载并安装最新版本的Node.js&#xff0c;npm会随Node.js一起安装。 2. 使用npm安装Yarn 打开命令提示符或PowerShell&#xff0c;运行以下命令来全局安装Yarn&#xff1a; …...