数据结构(二)——线性表(双链表)

news2024/11/15 11:10:57

2.3.3 双链表

单链表:单链表结点中只有一个指向其后继的指针,使得单链表只能从前往后依次遍历,无法逆向检索,有时候不太方便

双链表的定义:双链表结点中有两个指针prior和next,分别指向其直接前驱和直接后继
表头结点的prior域和尾结点的next域都是NULL。

双链表结点类型描述如下:

typedef struct DNode{            //定义双链表结点类型
    ElemType data;                //数据域
    struct DNode *prior, *next;    //前驱和后继指针
} DNode, *DLinklist;

双链表在单链表结点中增加了一个指向其前驱的指针 prior

双链表的初始化 (带头结点):

typedef struct DNode{       //D表示double
    ElemType data;     
    struct DNode *prior, *next;
}DNode, *DLinklist;

// 初始化双链表
bool InitDLinkList(Dlinklist &L){     
    L = (DNode *)malloc(sizeof(DNode));     //分配一个头结点
    if(L==NULL)            //内存不足,分配失败
        return false;    
    L->prior = NULL;   //头结点的prior指针永远指向NULL     
    L->next = NULL;    //头结点之后暂时还没有结点,置空   
    return true;
}

void testDLinkList(){  
    DLinklist L;       //L是一个链表  初始化双链表
    InitDLinkList(L);       
    ...
}

// 判断双链表是否为空
bool Empty(DLinklist L){   
    if(L->next == NULL)   
        return true;      
    else             
        return false;
}

双链表的插入操作: 

typedef struct DNode{     
    ElemType data;       
    struct DNode *prior, *next;
}DNode, *DLinklist;

bool InsertNextDNode(DNode *p, DNode *s){ 
    if(p==NULL || s==NULL)  
        return false;         
    s->next = p->next;       // 将结点s插入到结点p之后
    if (p->next != NULL)       // 判断结点p之后是否有后继结点  
        p->next->prior = s; //p结点的后置结点的前向指针指向新插入的s
    s->prior = p;   //把s的前向指针指向p结点
    p->next = s;     //把p结点的后向指针指向s结点
    return true;
}

双链表的删除操作:

// 删除p结点的后继结点
bool DeletNextDNode(DNode *p){   
    if(p==NULL)           
        return false;      
    DNode *q =p->next;       // 找到p的后继结点q 
    if(q==NULL)          //p没有后继就返回false
        return false;    
    p->next = q->next;   //q结点不是最后一个结点
    if(q->next != NULL) 
        q->next->prior=p;  
    free(q);             //释放结点空间
    return true;
}

// 销毁一个双链表
bool DestoryList(DLinklist &L){ 
    // 循环释放各个数据结点   
    while(L->next != NULL){    
        DeletNextDNode(L);      
        free(L);            //释放头结点
        L=NULL;             // 头指针指向NULL
    }
}

 双链表的遍历:

// 向后遍历
while(p!=NULL){    
    // 对结点p做相应处理    
    p = p->next;
}

// 向前遍历
while(p!=NULL){    
    // 对结点p做相应处理 
    p = p->prior;
}

// 跳过头结点的遍历
while(p->prior!=NULL){ 
    //对结点p做相应处理    
    p = p->prior;
}

双链表不可随机存取,按位查找、按值查找操作都只能用遍历的方式实现。时间复杂度O(n)

2.3.4 循环链表

循环单链表


循环单链表和单链表的区别在于,表中最后一个结点的指针不是NULL,而改为指向头结点,从而整个链表形成一个环
在循环单链表中,表尾结点*r的next域指向L,故表中没有指针域为NULL的结点,因此,循环单链表的判空条件不是头结点的指针是否为空,而是它是否等于头指针L。 

循环单链表的实现:

typedef struct LNode{           //定义单链表结点类型
    ElemType data;                  
    struct LNode *next;     //指针指向下一个元素
}DNode, *Linklist;

// 初始化循环单链表
bool InitList(LinkList &L){    
    L = (LNode *)malloc(sizeof(LNode));      //分配一个头结点
    if(L==NULL)             //内存不足,分配失败
        return false;    
    L->next = L;           // 最后一个结点的next指针指向头结点    
    return true;
}

// 判断循环单链表是否为空
bool Empty(LinkList L){    
    if(L->next == L)       
        return true;    
    else             
        return false;
}

// 判断结点p是否为循环单链表的表尾结点
bool isTail(LinkList L, LNode *p){ 
    if(p->next == L)          //看p结点的下一个结点是否是头结点
        return true;      
    else            
        return false;
}

单链表:从一个结点出发只能找到后续的各个结点
循环单链表:从一个结点出发可以找到其他任何一个结点 

循环双链表

循环双链表的初始化

typedef struct DNode{            
    ElemType data;           
    struct DNode *prior, *next;  
}DNode, *DLinklist;

// 初始化空的循环双链表
bool InitDLinkList(DLinklist &L){  
    L = (DNode *) malloc(sizeof(DNode));  //分配一个头结点
    if(L==NULL)            //内存不足,分配失败
        return false;    
    L->prior = L;           //头结点的prior指针指向最后一个结点
    L->next = L;         //最后一个结点的next指针指向头结点 
}

void testDLinkList(){
    //初始化循环双链表
    DLinkList L;
    InitDLinkList(L);
    //...
}

// 判断循环双链表是否为空
bool Empty(DLinklist L){   
    if(L->next == L)       
        return true;      
    else           
        return false;
}

// 判断结点p是否为循环双链表的表尾结点
bool isTail(DLinklist L, DNode *p){   
    if(p->next == L)        //结点的next指针指向头结点
        return true;     
    else            
        return false;
}

循环双链表的插入 

// 将结点s插入到结点p之后
bool InsertNextDNode(DNode *p, DNode *s){  
    s->next = p->next;   
    //循环双链表不用担心p结点的下一个结点为空   
    p->next->prior = s;  
    s->prior = p;     
    p->next = s;
}

  循环双链表的删除

// 删除p结点的后继结点
bool DeletNextDNode(DNode *p){  
    // 找到p的后继结点q       
    DNode *q =p->next;        
    //循环双链表不用担心q结点的下一个结点为空  
    p->next = q->next;    
    q->next->prior=p;    
    free(q);      
    return true;
}

2.3.5 静态链表

静态链表:用数组的方式实现的链表

优点:增、删操作不需要大量移动元素
缺点:不能随机存取,只能从头结点开始依次往后查找,容量固定不可变

适用场景:
①不支持指针的低级语言;
②数据元素数量固定不变的场景(如操作系统的文件分配表FAT)


静态链表是用数组来描述线性表的链式存储结构,结点也有数据域data和指针域next,与前面所讲的链表中的指针不同的是,这里的指针是结点在数组中的相对地址(数组下标),又称游标。

静态链表的定义

#define MaxSize 10        //静态链表的最大长度
struct Node{              //静态链表结构类型的定义  
    ElemType data;        //存储数据元素    
    int next;             //下一个元素的数组下标 即游标
};

// 用数组定义多个连续存放的结点
void testSLinkList(){    
    struct Node a[MaxSize];  //数组a作为静态链表, 每一个数组元素的类型都是struct Node    
    ...
}

 王道书上是这么定义的,侧重于强调 a 是一个静态链表而非数组。

#define MaxSize 10        //静态链表的最大长度
typedef struct{           //静态链表结构类型的定义       
    ELemType data;        //存储数据元素     
    int next;             //下一个元素的数组下标
}SLinkList[MaxSize];

void testSLinkList(){      
    SLinkList a;
}

静态链表基本操作实现

初始化静态链表:
把a[0]的next 设为-1
把其他结点的next 设为一个特殊值用来表示结点空闲,如-2

查找结点:
从头结点出发挨个往后遍历结点

插入位序为 i 的结点:
①找到一个空的结点,存入数据元素
②从头结点出发找到位序为 i-1 的结点
③修改新结点的 next
④修改 i-1 号结点的 next

删除某个结点:
①从头结点出发找到前驱结点
②修改前驱结点的游标
③被删除结点 next 设为 -2

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

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

相关文章

5分钟搞懂MySQL存储引擎

文章目录 什么是存储引擎👋?指定存储引擎✅查看mysql提供什么存储引擎查看mysql当前默认的存储引擎修改mysql默认的存储引擎设置表的存储引擎 常用存储引擎🧰InnoDBMyISAMMemoryInnoDB 和 MyISAM的区别 什么是存储引擎👋&#xff…

23万条数据集,可以用来区分钓鱼网站!

文章目录 一、何为钓鱼网站?二、数据集介绍引用数据集数据展示字段解释 三、数据分析数据读取使用ucimlrepo读取数据 四、下载地址 一、何为钓鱼网站? 在数字化时代,网络安全问题日益严重,其中钓鱼网站是一种常见的网络威胁。钓鱼…

基于Java (spring-boot)的进销存管理系统

一、项目介绍 首页,基础信息管理,备忘录,进销管理,仓库管理,系统管理 二、作品包含 三、项目技术 后端语言:Java 项目架构:B/S架构 数据库:MySQL 前端技术:Vue 后端技术&…

【C语言】比较两个字符串大小,strcmp函数

目录 一,strcmp函数 1,strcmp函数 2,函数头文件: 3,函数原型: 4,返回取值: 二,代码实现 三,小结 一,strcmp函数 1,strcmp函数 …

信道模拟器广泛应用于通信产业 我国企业竞争力不断提高

信道模拟器广泛应用于通信产业 我国企业竞争力不断提高 信道模拟器,模拟通信信道受环境因素影响产生各种特征的仪器,主要由接收电路、发射电路、模拟器、主控CPU等组成,可用于外场环境或者实验室环境中。 根据新思界产业研究中心发布的《202…

线性代数 --- 特征值与特征向量(下)

特征值与特征向量 Eigen Values & Eigen Vectors Part III:如何求解特征向量与特征值 The Key Equation 对于一般矩阵A,如何找到他的特征值与特征向量? Step I: Find λ first! 首先,我们有方程: 但这里有两个未知数&…

短视频解析接口分发系统

宝塔面板:Nginx系统 php7.2 Mysql 5.6-5.7 伪静态Thinkphp 上传文件直接访问域名安装即可 可以自备 听说后边要出saas去水印小程序 下载地址:https://pan.xunlei.com/s/VNskSEelfRVIzoSm5P5Rcw34A1?pwdqzhh# 接口演示: 前端演示…

前端入职配置新电脑!!!

前端岗位入职第一天到底应该做些什么呢?又该怎样高效的认识、融入团队?并快速进入工作状态呢?这篇文章就来分享一下,希望对即将走向或初入前端职场的你,能够有所帮助。内含大量链接,欢迎点赞收藏&#xff0…

GPU性能测试中的张量和矩阵运算

正文共:888 字 7 图,预估阅读时间:1 分钟 前面我们使用PyTorch将Tesla M4跑起来之后(成了!Tesla M4Windows 10AnacondaCUDA 11.8cuDNNPython 3.11),一直有个问题,那就是显存容量的问…

Springboot的配置文件及其优先级

配置文件 内置配置文件 配置文件的作用:修改SpringBoot自动配置的默认值;SpringBoot在底层都给我们自动配置好;SpringBoot使用一个全局的配置文件,配置文件名是固定的: application.propertiesapplication.yml 以上…

javaweb篇请求与相应的参数问题

目录 前言 简单传参设置 get请求无法识别 post请求 简单传参问题无法识别的解决问题 注意事项 改法 实体参数 代码展示 今日分享 前言 友友们,大家好,今天来开荒了,今天介绍的是在进行数据请求以及相应的时候,我们不仅仅只是进入一…

Java SE 抽象类与接口(二):接口(下)

2.5 实现多个接口 在Java语言中,类和类之间是单继承关系,一个类只可以有一个父类,即Java中不支持多继承关系,但是一个类可以实现多个接口,下面通过Animal类来具体说明 class Animal {protected String name;public A…

phpcms上传漏洞

原始漏洞 漏洞原理:我们上传一个zip的压缩包,它会解压然后删除其中不是.jpg .gig .png的文件 function check_dir($dir):这是一个PHP函数的定义,它接受一个参数 $dir,代表要检查的目录路径。 $handle opendir($dir)…

MySQL学习Day32——数据库备份与恢复

在任何数据库环境中,总会有不确定的意外情况发生,比如例外的停电、计算机系统中的各种软硬件故障、人为破坏、管理员误操作等是不可避免的,这些情况可能会导致数据的丢失、 服务器瘫痪等严重的后果。存在多个服务器时,会出现主从服…

C语言程序环境和预处理Pt.1 - 预处理指令|预处理操作符

电脑所能识别的语言为二进制指令,而我们所写的C语言代码是文本信息。为了能使计算机识别并执行C语言代码,就需要翻译环境,使C语言代码翻译为二进制的指令。 1.按下编译按钮的幕后 - 程序的翻译环境 从C语言源代码到计算机可识别的二进制文件…

【前端】 响应式布局

目录 1.媒体查询 2.BootStrap 2.1BootStrap引入 2.2BootStrap栅格系统 2.3BootStrap手册查询 1.媒体查询 响应式布局:显示区域改变,布局随之改变,即同一套代码适配不同大小的显示器 媒体查询:检测视口宽度,设置差…

案例分析篇12:可靠性设计考点(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章推荐: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html 【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例分析篇-…

信号与系统学习笔记——信号的分类

目录 一、确定与随机 二、连续与离散 三、周期与非周期 判断是否为周期函数 离散信号的周期 结论 四、能量与功率 定义 结论 五、因果与反因果 六、阶跃函数 定义 性质 七、冲激函数 定义 重要关系 作用 一、确定与随机 确定信号:可以确定时间函数…

【AIGC】重磅消息,GPT-4.5 Turbo将在6月发布?

2024 年 AI 辅助研发趋势 文章目录 强烈推荐GPT-4.5 Turbo竞争对手Anthropic的Claude 3谷歌的Gemini 1.5 Pro 总结强烈推荐专栏集锦写在最后 强烈推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击…

酷开系统走在前列,品牌重启增长,酷开科技成为品牌商合作目标

区别于火热的移动端,手机屏作为私密屏,往往面向的是用户个体,而电视作为家庭连接的重要枢纽,不仅仅定位于公共屏,同时也面向客厅场景发挥着其大屏传播的作用,这里不仅牵扯到大屏营销,也关联着大…