双链表的基本知识以及增删查改的实现

满怀热忱,前往梦的彼岸


前言

之前我们对单链表进行了非常细致的剖析,现在我们所面临的则是与之相对应的双链表,我会先告诉诸位它的基本知识,再接着把它的增删查改讲一下,ok,正文开始。

一.链表的种类

我们上一篇也说了。链表一共有八种,具体可以看下文。

【单链表实现通讯录(增删查改) - CSDN App】http://t.csdnimg.cn/EABuy

我们之前所说的单链表指的是不带头不循环单向链表,而双链表则是带头循环双向链表,只要会了这两个,那八种链表就都会了。

二.为什么引入双链表?

 单链表的结点中只有一个指向下一个节点的指针,使得单链表要访问某个结点的前驱结点时,只能从头开始遍历,时间复杂度为O(n)。为了克服上述缺点,引入了双链表。

 双链表的结点中有两个指针pre和next,分别指向前驱结点和后继结点。

typedef int LTDataType;

typedef struct LTNode
{
    LTDataType val;
    struct LTNode* next;
    struct LTNode* pre;
}LTNode;

三.双向链表的结构

带头链表⾥的头节点,实际为“哨兵位”,哨兵位节点不存储任何有效元素,只是站在这⾥“放哨
的”
“哨兵位”存在的意义
遍历循环链表避免死循环

四.双链表各个接口的实现


双链表初始化

void LTInit(LTNode** pphead)
{
    assert(pphead);
    *pphead = (LTNode*)malloc(sizeof(LTNode));
    (*pphead)->next = *pphead;
    (*pphead)->pre = *pphead;
    (*pphead)->val = -1;
}

双链表的销毁

void LTDestroy(LTNode* phead)
{
    assert(phead);
    LTNode* new = phead->next;
    while (new != phead)
    {
        LTNode* nnew = new->next;
        free(new);
        new = nnew;
    }
    free(phead);
}

双链表的内容打印

void LTPrint(LTNode* phead)
{
    assert(phead);
    LTNode* new = phead->next;
    while (new != phead)
    {
        printf("%d    ", new->val);
        new = new->next;
    }
    puts("");
}

判断双链表是否为空

bool LTEmpty(LTNode* phead)
{
    assert(phead);
    if (phead == phead->next)
        return true;
    return false;
}

双链表的尾插

void LTPushBack(LTNode* phead, LTDataType x)
{
    assert(phead);
    LTNode* new = creat(x);
    phead->pre->next =new;
    new->pre = phead->pre;
    new->next = phead;
    phead->pre = new;
}

双链表的尾删

void LTPopBack(LTNode* phead)
{
    assert(phead);
    if (phead->next == phead)
        return;
    LTNode* new = phead->pre;
    new->pre->next = phead;
    phead->pre = new->pre;
    free(new);
}

双链表的头插

void LTPushFront(LTNode* phead, LTDataType x)
{
    LTNode* new = creat(x);
    new->next = phead->next;
    phead->next->pre = new;
    phead->next = new;
    new->pre = phead;
}

双链表的头删

void LTPopFront(LTNode* phead)
{
    assert(phead);
    if(phead->next != phead)
    printf("链表已经空了\n");
    else
    {
        LTNode* new = phead->next;
        phead->next = new->next;
        new->next->pre = phead;
        free(new);
    }
}

指定位置的插入

//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{
    LTNode* new = creat(x);
    new->next = pos->next;
    pos->next->pre = new;
    pos->next = new;
    new->pre = pos;
}

指定位置的删除

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

数据查找

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

创建给定数据对应的双链表指针

LTNode* creat(LTDataType x)
{
    LTNode* p = (LTNode*)malloc(sizeof(LTNode));
    p->next = p;
    p->pre = p;
    p->val = x;
    return p;
}
​​​​​​ 

五. 顺序表和双向链表的优缺点分析

   不同点                                   链表                             顺序表                                   
存储空间上                                    物理上⼀定连续         逻辑上连续,理上不⼀定连续
随机访问                                        ⽀持O(1)                     不⽀持:O(N)
任意位置插⼊或者删除元素           可能要搬移元素,效率低O(N)               只需修改指针指向
插⼊                                 动态顺序表,空间不够时要扩容      没有容量的概念
应⽤场景                          元素⾼效存储+频繁访问                任意位置插⼊和删除频繁

总结

ok,到这里链表算是过去了,我们翻过了数据结构的两座山,第一座山是顺序表,当时我们是以通讯录作为结尾,第二座是链表,我们写了单链表的增删查,又在此基础上写了通讯录,现在有写出了双链表的增删查改等功能,通讯录的话,写了那么多次了,就不写了,咱写个用链表造出来的贪吃蛇,我之前写过过一个,但那个没有用链表,而且功能也不齐全,这次写个完全版,作为C语言的结尾和数据结构的开篇。


那么,敬请期待,下一篇博客。

感觉有帮助的话,就点个赞支持一下吧。

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

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

相关文章

其他发现:开源数据可视化分析工具DataEase介绍文档

一、 简介 DataEase 是开源的数据可视化分析工具,帮助用户快速分析数据并洞察业务趋势,从而实现业务的改进与优化。DataEase 支持丰富的数据源连接,能够通过拖拉拽方式快速制作图表,并可以方便地与他人分享。 二、 优势 1、 开…

STM32学习笔记二——STM32时钟源时钟树

目录 STM32芯片内部系统架构详细讲解: 1.芯片内部混乱电信号解决方案: 2.时钟树: 1.内部RC振荡器与外部晶振的选择 2. STM32 时钟源 3.STM32中几个与时钟相关的概念 4.时钟输出的使能及其流程 5.时钟设置的基本流程 时钟源——单片机…

Java多线程--同步机制解决线程安全问题方式二:同步方法

文章目录 一、同步方法(1)同步方法--案例11、案例12、案例1之同步监视器 (2)同步方法--案例21、案例2之同步监视器的问题2、案例2的补充说明 二、代码及重要说明(1)代码(2)重要说明 …

基于yolov2深度学习网络的视频手部检测算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 输入mp4格式的视频文件进行测试,视频格式为1080p30. 2.算法运行软件版本 matlab2022a 3.部分核心程序 ..........................…

Linux第40步_移植ST公司的uboot

一、查看ST公司的uboot源码包 ST公司的uboot源码包在虚拟机中的路径: “/home/zgq/linux/atk-mp1/stm32mp1-openstlinux-5.4-dunfell-mp1-20-06-24/sources/arm-ostl-linux-gnueabi/u-boot-stm32mp-2020.01-r0”; “u-boot-stm32mp-2020.01-r0”就是S…

Github 上传项目(个人令牌token)

1.点击 github头像 : setting -> Developer Settings -> Personal access tokens 2.在要上传的文件夹下运行以下命令: git init git commit -m "first commit" git branch -M main 利用以下命令模…

Vue中嵌入原生HTML页面

Vue中嵌入html页面并相互通信 需求&#xff1a;b2b支付需要从后获取到数据放到form表单提交跳转&#xff0c;如下&#xff1a; 但是vue目前暂时没找到有类似功能相关文档&#xff0c;所以我采用iframe嵌套的方式 1. Vue中嵌入Html <iframe src"/static/gateway.htm…

多线程c++

目录 1.join和detach区别 2.lock_guard和unique_lock 3.原子操作 4.条件变量condition_variable 5.future 和 promise 1.join和detach区别 ①不使用join和detach #include <iostream> #include <thread> #include <windows.h>using namespace std;v…

hcip---ospf综合实验

一&#xff1a;实验要求 1、R4为ISP&#xff0c;其上只能配置IP地址&#xff0c;R4与其所有直连设备间均使用公有IP 2、R3-R5/6/7为MGRE环境&#xff0c;R3为中心站点 3、整个OSPF环境IP基于R4的环回 4、所有设备均可访问R4的环回 5、减少LSA的更新量&#xff0c;加快收敛…

医院如何筛选安全合规的内外网文件交换系统?

医院内外网文件交换系统是专为医疗机构设计的&#xff0c;用于在内部网络&#xff08;内网&#xff09;和外部网络&#xff08;外网&#xff09;之间安全、高效地传输敏感医疗数据和文件的解决方案。这种系统对于保护患者隐私、遵守医疗数据保护法规以及确保医疗服务的连续性和…

初探分布式链路追踪

本篇文章&#xff0c;主要介绍应用如何正确使用日志系统&#xff0c;帮助用户从依赖、输出、清理、问题排查、报警等各方面全面掌握。 可观测性 可观察性不单是一套理论框架&#xff0c;而且并不强制具体的技术规格。其核心在于鼓励团队内化可观察性的理念&#xff0c;并确保由…

Django4.2(DRF)+Vue3 读写分离项目部署上线

文章目录 1 前端2 后端2.1 修改 settings.py 文件关于静态文件2.2 关于用户上传的文件图片 3 Nginx4 镜像制作4.1 nginx4.3 Django镜像4.3.1 构建 5 docker-compose 文件内容 1 前端 进入前端项目的根目录&#xff0c;运行如下命令进行构建 npm run build构建完成后&#xff…

K8S之Pod的介绍和使用

Pod的理论和实操 pod理论说明Pod介绍Pod运行与管理Pod管理多个容器Pod网络Pod存储 Pod工作方式自主式Pod控制器管理的Pod&#xff08;常用&#xff09; 创建pod的流程 pod实操通过资源清单文件创建自主式pod通过kubectl run创建Pod&#xff08;不常用&#xff09; pod理论说明 …

指针的深入了解6

1.回调函数 回调函数就是一个通过函数指针调用的函数。 如果你把函数的指针&#xff08;地址&#xff09;作为参数传递给另一个函数&#xff0c;当这个指针被用来调用其所指向的函数 时&#xff0c;被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用&#xff0…

【LVGL源码移植环境搭建】

LVGL源码移植&环境搭建 ■ LVGL源码移植■ 下载LVGL源码■ 修改LVGL文件夹■■■■ 视频链接 Ubuntu模拟器环境建置 ■ LVGL源码移植 ■ 下载LVGL源码 LVGL源码 我们以选择v8.2.0为例&#xff0c;选择8.2.0下载 ■ 修改LVGL文件夹 1.我们只需要关注这5个文件即可&…

《Docker技术革命:从虚拟机到容器化,全面解析Docker的原理与应用-上篇》

文章目录 Docker为什么会出现总结 Docker的思想Docker历史总结 Docker能干嘛虚拟机技术虚拟机技术的缺点 容器化技术Docker和虚拟机技术的区别 Docker概念Docker的基本组成镜像&#xff08;image)容器&#xff08;container&#xff09;仓科&#xff08;repository&#xff09;…

GitHub工作流的使用笔记

文章目录 前言1. 怎么用2. 怎么写前端案例1&#xff1a;自动打包到新分支前端案例2&#xff1a;自动打包推送到gitee的build分支案例3&#xff1a;暂时略 前言 有些东西真的就是要不断的试错不断地试错才能摸索到一点点&#xff0c;就是摸索到凌晨两三点第二天要8点起床感觉要…

聊一聊GPT、文心、通义、混元

我使用同一个Prompt提示词“请以记叙文的文体来写”&#xff0c;分别发送给GPT-3.5&#xff08;调用API&#xff09;、文心、通义、混元&#xff0c;下面是它们各自生成的文本内容&#xff0c;大家一看便知了。 GPT-3.5&#xff1a; 在我个人使用GPT模型的过程中&#xff0c;我…

Facebook的创新征程:社交媒体的演进之路

在当今数字化时代&#xff0c;社交媒体已经成为人们生活中不可或缺的一部分&#xff0c;而Facebook作为社交媒体领域的巨头&#xff0c;一直在不断创新和演进。本文将深入探讨Facebook的创新征程&#xff0c;追溯其社交媒体的发展历程&#xff0c;探讨其对用户、社会和数字时代…

echart 实现自定义地图

先上效果图 需求&#xff1a;自定义区域平面图&#xff0c;支持区域高亮 // 2D详情const initChartsMapItemB async (flow: any, mapbg: any) > {// mapbg 为svg的地址 import mapbg from //assets/json/map/F42d.svgconst svgData (await request.get(mapbg)) as anye…