一文了解双向链表

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、链表分类
  • 二、双向链表是什么?
  • 三、功能函数实现
    • 1.申请一个节点
    • 2.初始化
    • 3.尾插
    • 4.头插
    • 5.尾删
    • 6.头删
    • 7.在指定位置后插入
    • 8.删除指定位置数据
    • 9.查找
    • 10.销毁
  • 四、整体代码
    • 1.头文件
  • 总结


在这里插入图片描述

前言

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。今天我们就先来学习一下链表中的双向链表。


一、链表分类

在这里插入图片描述
有头(哨兵位):在链表的最前端有一个节点,这个节点的数据没有意义,这个节点充当链表的头部进行维护。
循环:链表的第一个节点和最后一个节点是否相连。
双向,单向:从一个节点如果可以找到前一个节点和后一个节点就是双向,如果只能找到后一个节点,就是单向。
这三个特征进行组合就可以组合出8种链表。而今天的双链表就是有头循环双向的链表。博主上一篇的单链表就是无头不循环单向链表。

二、双向链表是什么?

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。
其结构如图
在这里插入图片描述
如下是一个节点的定义,也是下文代码实现中的节点定义

typedef int LTDataType;
typedef struct ListNode
{LTDataType* data;//储存数据struct ListNode* prev;//指向上一个节点struct ListNode* next;//指向下一个节点
}LTNode;

三、功能函数实现

1.申请一个节点

代码如下(示例):

LTNode* LTBuyNode(LTDataType x)
{LTNode* node = (LTNode*)malloc(sizeof(LTNode));if (node == NULL){perror("malloc");exit(1);}node->data = x;node->next = node->prev = node;return node;
}

在一个节点中需要两个数据,一个是数值,一个是下一节点的地址。所以我们申请新节点时就传入该节点中的数值(参数),之后使用malloc申请空间并判断是否成功,将data赋值,next指针和prev指针都指向自己(因为这是一个循环结构),最后返回节点地址。
在这里插入图片描述

2.初始化

代码如下(示例):

LTNode* LTInit()
{LTNode* phead = LTBuyNode(-1);return phead;
}

初始化其实就是设置哨兵位,我们暂时赋-1,这里写成多少都可以。最后返回哨兵位地址,以后的增删改查都与他有关。

3.尾插

void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTBuyNode(x);newnode->next=phead;newnode->prev = phead->prev;phead->prev->next = newnode;phead->prev = newnode;
}

在这里插入图片描述
尾插就是将新节点插在最后,但因为链表循环,所以尾插也就是插在头节点前面,首先创建新节点,再将新节点的next指针指向头节点,其prev指针指向原本头节点前面的节点。完成后将原本头节点前一个节点的节点的next指向新节点,将头节点的prev指向新节点。

4.头插

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;
}

在这里插入图片描述
头插的位置如图是head和d1节点中间的位置,其插入逻辑和尾插一样就不再赘述,主要理解头插的位置。

5.尾删

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

在这里插入图片描述

尾删实际上就是删除头节点的上一个节点,这里我们定义一个del指针记录删除节点的地址方便使用。首先先将删除节点的前一个节点的next指针指向头节点,再将头节点的prev指针指向新尾节点,这样就摘除了原来的尾节点,最后通过del释放尾节点。

6.头删

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

在这里插入图片描述
头删的位置如图是d1节点的位置,其插入逻辑和尾删一样就不再赘述,主要理解头删的位置。

7.在指定位置后插入

void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newnode = LTBuyNode(x);newnode->prev = pos;newnode->next = pos->next;pos->next->prev = newnode;pos->next = newnode;
}

在这里插入图片描述
在指定位置处插入,其逻辑如图所示,因为这一操作需要调整d1,d2,newnode三个节点,所以我们先调整新节点的指针指向,因为对新指针的更改不影响原链表。接着将pos下一个节点prev指向新节点,pos前一个节点next指向新节点。

8.删除指定位置数据

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

在这里插入图片描述
删除指定位置的逻辑和插入指定数据与尾删相似,可以类比理解。

9.查找

LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);LTNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}

查找是比较容易的函数,只要遍历整个链表并进行比较即可,但注意循环停止的条件,当pcur遍历一遍链表回到头结点时,循环结束。

10.销毁

void LTDesTroy(LTNode* phead)
{LTNode* pcur = phead->next;while (pcur != phead){LTNode* next = pcur->next;free(pcur);pcur = next;}free(phead);phead = NULL;
}

销毁和整体逻辑就是一边遍历,一边一个一个的释放节点,还是注意循环停止条件和避免出现空指针。

四、整体代码

1.头文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>typedef int LTDataType;
typedef struct ListNode
{LTDataType* data;struct ListNode* prev;struct ListNode* next;
}LTNode;LTNode* LTInit();
void LTDesTroy(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);
//删除pos节点
void LTErase(LTNode* pos);
LTNode* LTFind(LTNode* phead, LTDataType x);

总结

以上就是作者对单链表的一些理解和介绍,希望看到这篇文章的朋友们可以积极评价,还请一键三连。
在这里插入图片描述

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

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

相关文章

带环链表问题

带环链表就是字面意思带环的链表&#xff0c;例如以下这三种情况 练习题 1.给定一个链表&#xff0c;判断链表中是否带环. - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;快慢指针&#xff0c;慢指针走一步&#xff0c;快指针走两步&#xff0c;两个指针从链表的起…

【项目经验】Redis Sentinel从工程中下线并对业务迁移-(二)

在上篇文章【项目经验】Redis Sentinel从工程中下线并对业务迁移-进行中-CSDN博客有说到迁移的计划。最近一直按照计划进行迁移&#xff0c;期间遇到了不少问题。总结如下&#xff1a; 一、key未设置过期时间 redis基于内存存储&#xff0c;主要作用是缓存。当大量的key未设置…

nginx的前世今生(二)

书接上回&#xff1a; 上回书说到&#xff0c;nginx的前世今生&#xff0c;这回我们继续说 3.缓冲秘籍&#xff0c;洪流控水 Nginx的缓冲区是其处理数据传输和提高性能的关键设计之一&#xff0c;主要用于暂存和管理进出的数据流&#xff0c;以应对不同组件间速度不匹配的问题…

池化整合多元数据库,zData X 一体机助力证券公司IT基础架构革新

引言 近期&#xff0c;云和恩墨 zData X 多元数据库一体机&#xff08;以下简称 zData X&#xff09;在某证券公司的OA、短信和CRM业务系统中成功上线&#xff0c;标志着其IT基础架构完成从集中式存储向池化高性能分布式存储的转变。zData X 成功整合了该证券公司使用的达梦、O…

Windows php 安装 Memcached扩展、php缺失 Memcached扩展、Class ‘Memcached‘ not found

在Windows系统下如何安装 php Memcached 扩展 下载dll文件 pecl地址&#xff1a;https://pecl.php.net/package/memcached 根据版本进行选择 &#xff1a; 解压下载的文件后得到了这么样的文件结构&#xff1a; 配置 移动dll文件到相应文件位置 重点&#xff1a; libme…

FreeRTOS队列集(1-15)

队列集定义&#xff1a;def 队列集只允许任务间传递消息为同一种数据类型&#xff0c;如果需要在任务间传递不同数据类型的消息时&#xff0c;就可以使用队列集。 用于对多个信号量进行监听&#xff0c;其中不管哪一个消息到来&#xff0c;都可以让任务退出阻塞状态 假设&am…

如何利用MCU自动测量单元提高大坝安全监测效率

大坝作为重要的水利基础设施&#xff0c;其安全性直接关系到人民群众的生命财产安全和社会的稳定发展。因此&#xff0c;对大坝进行实时、准确的安全监测至关重要。近年来&#xff0c;随着微控制器单元(MCU)技术的不断发展&#xff0c;其在大坝安全监测领域的应用也越来越广泛。…

springboot配置WebMvcConfigurationSupport

一、在spring里有四个mvc配置类 1、mvc配置类 WebMvcConfigurer WebMvcConfigurerAdapter WebMvcConfigurationSupport WebMvcAutoConfiguration 2、WebMvcConfigurer为接口 3、WebMvcConfigurerAdapter是WebMvcConfigurer的实现类&#xff0c;且大部分为空方法&#xff0c;…

微服务架构面试题(二)

1. 请简述微服务中各组件的作用 &#xff1f; 微服务架构中的组件各自扮演着关键的角色&#xff0c;它们协同工作以构建高效、可扩展和灵活的系统。以下是一些核心组件及其作用&#xff1a; 服务注册与发现组件&#xff1a;如Eureka&#xff0c;负责让其他服务知道当前服务的…

使用 Flask、Gunicorn 与 Shell 脚本构建高效 Web 应用部署流程

在使用 Flask 作为 Web 应用框架&#xff0c;并使用 Gunicorn 作为 WSGI 容器&#xff0c;使用shell 脚本来管理应用的启动、重启和停止。 启动脚本 start.sh&#xff1a; #!/bin/bash# 设置应用名称和端口 APP_NAME"my_flask_app" PORT8000# 设置 Flask 应用的路径…

【PCL】教程 supervoxel_clustering执行超体聚类并可视化点云数据及其聚类结果

[done, 417.125 ms : 307200 points] Available dimensions: x y z rgba 源点云milk_cartoon_all_small_clorox.pcd > Loading point cloud... > Extracting supervoxels! Found 423 supervoxels > Getting supervoxel adjacency 这段代码主要是使用PCL&#xff08;Po…

【Linux】创建/扩容swap交换空间swap优化

一、当前交换空间大小 目前交换空间大小为2G 二、创建swap交换空间 #创建大小为2G的交换空间 [roothadoop01 data1]# dd if/dev/zero of/data1/swapfile bs1M count2048 #将文件设置为交换空间 [roothadoop01 data1]# mkswap /data1/swapfile #启用交换空间 [roothadoop01 da…

【Python】pandas.cut()函数的用法

pandas.cut() 函数是一个非常有用的工具&#xff0c;用于将数值型数据按照指定的分箱或区间进行分割&#xff0c;从而将连续的数值变量转换为离散的类别变量。这在数据分析和机器学习的特征工程中尤其有用&#xff0c;因为它可以帮助揭示不同区间内的数据分布特征&#xff0c;或…

【Qt问题】Qt Creator 如何链接第三方库

往期回顾 【QT问题】 Qt信号函数如果重名&#xff0c;调用怎么处理-CSDN博客 【Qt问题】Qt中文乱码问题解决方案&#xff08;详细汇总&#xff09;-CSDN博客 【Qt问题】Qt常用快捷键汇总-CSDN博客 【Qt问题】Qt Creator 如何链接第三方库 一、为什么会使用第三方库 在编写代码…

Java Web 开发 - 掌握拦截器和监听器

目录 深入了解Java Web的拦截器和监听器 拦截器&#xff08;Interceptor&#xff09; 拦截器的使用场景 拦截器实例 思维导图 ​编辑 监听器&#xff08;Listener&#xff09; 监听器的使用场景 监听器类型 监听器实例 思维导图​编辑 总结 深入了解Java Web的拦截器…

在UI界面中播放视频_unity基础开发教程

在UI界面中播放视频_unity基础开发教程 前言操作步骤结语 前言 之前我写过一篇在场景中播放视频的文章&#xff0c;但是在开发中有时候也会在UI的界面中播放视频&#xff0c;这期我们做一下在UI的界面中播放视频。 操作步骤 首先在场景中创建一个Raw Image&#xff0c;UI->…

DAO是什么?有什么用途?

DAO&#xff08;Decentralized Autonomous Organization&#xff0c;去中心化自治组织&#xff09;是一种基于区块链技术的组织形式&#xff0c;它没有中央管理层&#xff0c;而是通过智能合约和区块链上的代码来运作。DAO 的决策过程是透明的&#xff0c;通常由组织的成员通过…

java学习笔记11

20. 字符串类 字符串是指一连串的字符,它是由许多单个字符连接而成。字符串可以包含任意字符,这些字符必须包含在一对双引号""之内,例如:“abc”.java中封装了3个字符串类,分别是String类、StringBuffer类、StringBuilder类,都在java.lang包中。20.1 String类的…

孤独的城市公路(增加开始结束页面)

致敬 裴总 缺点 没有一个车没有背景音乐后期都会添加 为什么裴总良心 裴总真良心 还有音乐 还是3D 还有开始按钮 还能game 还有结尾 还有结尾提示 import arcadeSCREEN_WIDTH 700 SCREEN_HEIGHT 700 SCREEN_TITLE "孤独的城市公路" MOVEMENT_SPEED 5 …

go http框架下的静态资源代理实现(压缩,缓存验证自定义)

之前在这一篇文章里说了我的第一版静态资源代理&#xff0c;后面我又完善了一下&#xff1a; 上一种方案的问题&#xff1a; 首页未加入自定义代理中依赖了gin框架的file()方法反复访问本地文件&#xff0c;访问文件系统是很消耗性能的 所以本次我做了改进&#xff0c;思路是…