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

news2024/11/15 21:39:48

满怀热忱,前往梦的彼岸


前言

之前我们对单链表进行了非常细致的剖析,现在我们所面临的则是与之相对应的双链表,我会先告诉诸位它的基本知识,再接着把它的增删查改讲一下,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.coloradmin.cn/o/1421014.html

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

相关文章

07.领域驱动设计:掌握整洁架构、六边形架构以及3种常见微服务架构模型的对比和分析

目录 1、概述 2、整洁架构 3、六边形架构 4、三种微服务架构模型的对比和分析 5、从三种架构模型看中台和微服务设计 5.1 中台建设要聚焦领域模型 5.2 微服务要有合理的架构分层 5.2.1 项目级微服务 5.2.2 企业级中台微服务 5.3 应用和资源的解耦与适配 6、总结 1、概…

三步万能公式解决软件各种打不开异常

程序员都知道,辛苦做的软件发给客户打不开那是一个大写的尴尬,尴尬归尴尬还是要想办法解决问题. 第一步清理环境. 目标机台有环境和没有运行环境的,统统把vs环境卸载了,让目标机台缺少环境.第二步打包环境 源代码添加打包工程,setup,重新编译.![添加setup ](https://img-blo…

LeetCode——415. 字符串相加

C开头 😶‍🌫️😶‍🌫️😶‍🌫️😶‍🌫️Take your time ! 😶‍🌫️😶‍🌫️😶‍🌫️😶‍&#…

【架构论文】SCALE: Secure and Scalable Cache Partitioning(2023 HOST)

SCALE: Secure and Scalable Cache Partitioning 摘要 LLC可以提高性能,但是会引入安全漏洞,缓存分配的可预测变化可以充当侧信道,提出了一种安全的缓存分配策略,保护缓存免受基于时间的侧信道攻击。SCALE使用随机性实现动态可扩…

AI大模型专题:2024大模型安全流通平台市场厂商评估报告

今天分享的是AI大模型系列深度研究报告:《AI大模型专题:2024大模型安全流通平台市场厂商评估报告》。 (报告出品方:揽睿星舟) 报告共计:22页 大模型安全流通平台市场分析 企业需要大模型安全流通平台覆盖…

C语言 开发篇+一个简单的数据库管理系统ZDB

说明:本文供数据库爱好者和初级开发人员学习使用 标签:数据库管理系统、RDBMS、C语言小程序、C语言、C程序 系统:Windows 11 x86 CPU :Intel IDE :CLion 语言:C语言 标准:C23 提示:如…

C语言用SHBrowseForFolder弹出选择文件夹的对话框

【程序运行效果】 【程序代码】 main.c&#xff1a; #define COBJMACROS #include <stdio.h> #include <tchar.h> #include <Windows.h> #include <windowsx.h> #include <CommCtrl.h> #include <ShlObj.h> #include "resource.h&q…

JVM-类的生命周期

类的生命周期概述 类的生命周期描述了一个类加载、使用、卸载的整个过程。整体可以分为&#xff1a; 加载 连接&#xff0c;其中又分为验证、准备、解析三个子阶段 初始化 使用 卸载 加载阶段 加载(Loading)阶段第一步是类加载器根据类的全限定名通过不同的渠道以二进制流的方…

深入玩转Playwright:高级操作解析与实践

playwright高级操作 iframe切换 ​ 很多时候&#xff0c;网页可能是网页嵌套网页&#xff0c;就是存在不止一个html标签&#xff0c;这时候我们的selenium或者playwright一般来说定位不到&#xff0c;为什么呢&#xff1f; ​ 因为默认是定位到第一个标准的html标签内部。 …

费一凡:土木博士的自我救赎之道 | 提升之路系列(五)

导读 为了发挥清华大学多学科优势&#xff0c;搭建跨学科交叉融合平台&#xff0c;创新跨学科交叉培养模式&#xff0c;培养具有大数据思维和应用创新的“π”型人才&#xff0c;由清华大学研究生院、清华大学大数据研究中心及相关院系共同设计组织的“清华大学大数据能力提升项…

五、防御保护---防火墙出口选路篇

五、防御保护---防火墙智能选路篇 一、就近选路二、策略路由选路1.策略路由的概念1.1匹配条件&#xff08;通过ACL定义&#xff09;1.2动作 三、智能选路 --- 全局路由策略1.基于链路带宽的负载分担2.基于链路质量进行负载分担3.基于链路权重进行负载分担4.基于链路优先级的主备…

股票市场

&#xff08;一&#xff09;股票市场 顾名思义&#xff0c;就是买卖股票的场所。就是为了撮合想发展但缺钱的企业与有钱但想投资的投资者。 股票市场按照交易场所&#xff0c;可分为场内市场和场外市场&#xff1a; 场内市场是指证券交易所&#xff0c; 场外市场就是证券交易…

比Filebeat更强大的日志收集工具-Fluent bit的http插件实战

文章目录 1.前言2. fluent bit http插件配置以及参数详解3. Http 接口服务3.1 开发Http 接口服务3.2 重启fluent bit向http web服务发送数据 1.前言 Fluent Bit 的 HTTP 插件提供了一种灵活而通用的机制&#xff0c;可用于将日志数据 从各种环境中传输到指定的远程服务器&#…

Python算法题集_滑动窗口最大值

本文为Python算法题集之一的代码示例 题目239&#xff1a;滑动窗口最大值 说明&#xff1a;给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗…

常见的网络安全威胁和防护方法

随着数字化转型和新兴技术在各行业广泛应用&#xff0c;网络安全威胁对现代企业的业务运营和生产活动也产生了日益深远的影响。常见的网络安全威胁通常有以下几种&#xff1a; 1. 钓鱼攻击 攻击者伪装成合法的实体&#xff08;如银行、电子邮件提供商、社交媒体平台等&#xf…

C++实现通讯录管理系统

目录 1、系统需求 2、创建项目 2.1 创建项目 3、菜单功能 4、退出功能 5、添加联系人 5.1 设计联系人结构体 5.2 设计通讯录结构体 5.3 main函数中创建通讯录 5.4 封装联系人函数 5.5 测试添加联系人功能 6、显示联系人 6.1 封装显示联系人函数 7、删除联系人 7.1…

获取依赖aar包的两种方式-在android studio里引入 如:glide

背景&#xff1a;我需要获取aar依赖到内网开发&#xff0c;内网几乎代表没网。 一、 如何需要获取依赖aar包 方式一&#xff1a;在官方的github中下载,耗时不建议 要从开发者网站、GitHub 存储库或其他来源获取 ‘com.github.bumptech.glide:glide:4.12.0’ AAR 包&#xff…

MySQL:MVCC原理详解

MySQL是允许多用户同时操作数据库的&#xff0c;那么就会出现多个事务的并发场景。那么再并发场景会出现很多问题&#xff1a;脏读、不可重复读、幻读的问题。 而解决这些问题所用到的方法就是&#xff1a;MVCC 多版本并发控制。而这个MVCC的实现是基于read_view、undoLog 如…

大规模机器学习(Large Scale Machine Learning)

1.大型数据集的学习 案例&#xff1a; 如果我们有一个低方差的模型&#xff0c;增加数据集的规模可以帮助你获得更好的结果。我们应该怎样应对一个有 100 万条记录的训练集&#xff1f; 以线性回归模型为例&#xff0c;每一次梯度下降迭代&#xff0c;我们都需要计算训练集的误…

古建筑电气火灾的防控与管理

摘要:我国古建筑多为砖木结构&#xff0c;当发生火灾事故时具有蔓延快、扑救难的特点&#xff0c;而火灾对古建筑的损害性很大&#xff0c;电气火灾事故在我国火灾事故中比重居高不下。本文通过对古建筑电气火灾成因进行分析&#xff0c;有针对性地提出了古建筑电气火灾防控对策…