数据结构杂谈(四)

本文的所有代码均由C++编写

4 双链表、循环链表和静态链表

文章目录

  • 4 双链表、循环链表和静态链表
    • 4.1 双链表
      • 4.1.1 双链表的定义
      • 4.1.2 双链表的初始化
      • 4.1.2 双链表的后插操作
      • 4.1.3 双链表的后删操作
      • 4.1.4 双链表的销毁操作
    • 4.2 循环链表
      • 4.2.1 循环链表的概念
      • 4.2.2 循环单链表
        • 4.2.2.1 循环单链表的初始化
        • 4.2.2.2 循环单链表判空
        • 4.2.2.3 判断表尾结点
      • 4.2.3 循环双向链表
        • 4.2.3.1 循环双链表的初始化
        • 4.2.3.2 循环双链表的判空
        • 4.2.3.3 循环双链表的插入
        • 4.2.3.4 循环双链表的删除
    • 4.3 静态链表
      • 4.3.1 静态链表的定义
      • 4.3.2 静态链表的初始化
      • 4.3.3 静态链表的插入
      • 4.3.4 静态链表的删除
      • 4.3.5 静态链表后话

4.1 双链表

4.1.1 双链表的定义

在前面的知识中我们曾经说过,由于单链表中每个数据元素分为两部分——数据域和指针域,所以单链表只能寻找下一个元素而无法寻找上一个元素,即无法逆向检索,为了解决这个问题,我们引入了双链表。

双链表的节点用DNode(D指的是Double)命名,其在原有的数据域和指针域的基础上加了一个前置指针域(prior)。定义如下所示:

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

4.1.2 双链表的初始化

双链表对于单链表来说,初始化就有所不同了。

bool InitDLinkList(DLINKLIST &L){L = new DNode;if(L == NULL)return false;L->prior = NULL;L->next = NULL;return true;
}

4.1.2 双链表的后插操作

同样地,对于单链表熟悉插入操作的同学,双链表插入也会很熟悉,但是需要注意一点的同样是修改指针顺序问题。

bool InsertNextDNode(DNode *p,DNode *s){if(p == NULL || s== NULL)retur false;s->next = p->next;if(p->next != NULL)p->next->prior = s;s->prior = p;p->next = s;return true;
}

4.1.3 双链表的后删操作

bool DeleteNextDNode(DNode *p){if(p == NULL)return false;DNode *q = p->next;if(q == NULL)return false;p->next = q->next;if(q->next != NULL)q->next->prior = p;delete(q);return true;
}

4.1.4 双链表的销毁操作

void DestoryList(DLinklist &L)
{while(L->next != NULL)DeleteNextDNode(L);delete(L);L=NULL;
}

4.2 循环链表

4.2.1 循环链表的概念

在开篇,先让我们了解一下接下来要讲的循环列表的概念。

循环链表:是一种头尾相接的链表(即:表中最后一个结点的指针域指向头结点,整个链表形成一个环)。

image-20211026220027593

需要注意的是,由于循环链表中没有NULL指针,故涉及遍历操作时,其终止条件就不再像非循环链表那样判断p或p->next是否为空,而是判断它们是否等于头指针。

//循环条件
单链表:
p! = NULL
p->next != NULL//循环单链表:
p! = L
p->next! = L

4.2.2 循环单链表

在前面学习单链表的时候我们知道,如果对于某一个结点p,其前驱结点是不知道在哪的。但是对于循环单链表来说,从表中任一结点出发均可找到表中其他结点,因为表是循环的。

对于普通的单链表来说,从头结点找到尾部,时间复杂度是O(n);对于循环链表来说同样如此,但是如果你是从尾部开始找到头部,那么时间复杂度实际为O(1)。所以对于很多操作如果需要频繁地对表头表尾动手,那你可以在初始化工作的时候让L指针指向表尾元素。

4.2.2.1 循环单链表的初始化

bool InitList(LinkList &L){L = new LNode; //分配一个头结点if(L == NULL) //内存不足分配失败return false;L->next = L; //头结点next指向头结点return true;
}

4.2.2.2 循环单链表判空

bool Empty(LinkList L){if(L->next == L)return true;elsereturn false;
}

4.2.2.3 判断表尾结点

bool isTail(LinkList L,LNode *p){if(p->next == L)return true;elsereturn false;
}

4.2.3 循环双向链表

虽然循环单链表可以从任意一个结点开始找到任意一个结点,但是如果要查找的结点刚好是自己所处的前一个结点,那岂不是浪费时间兜圈子?为此,和单链的循环表类似,双向链表也可以有循环表,我们叫做循环双向表。如果一旦发生上述情况,直接通过前置指针即可访问。

4.2.3.1 循环双链表的初始化

对于其定义,我们上个小节谈论过了,如下:

//循环双链表的定义
typedef struct DNode
{int data;struct DNode* prior,*next;
}DNode,*DLinklist;

对于其初始化如下:

//初始化循环双链表
bool InitDList(DLinklist& L)
{L = new DNode;if (L == NULL)return false;L->prior = L;L->next = L;return true;
}

对于初始化双向链表来说,首先要注意内存空间不足的问题,这是在前面的单链表初始化中也需要注意的;第二个不一样的点就是:由于是循环链表,其后继指针域指向的不是空,而需要指自身;同样地,前驱指针域也是如此。

4.2.3.2 循环双链表的判空

如果要判断双链表是否为空,那无非就是判断是否像初始化时一样,前驱指针后继指针都指向头结点,如下:

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

4.2.3.3 循环双链表的插入

如果要对循环双链表做插入操作,需要注意改结点不止改一根指针了。如果要在循环双链表中的结点p后插入结点s,如下所示:

bool InsertNextDNode(DNode* p, DNode* s)
{s->next = p->next;p->next->prior = s;s->prior = p;p->next = s;
}

4.2.3.4 循环双链表的删除

对于删除,和插入一样:

//删除p->next = q->next;q->next->prior = p;free(q);

2.5.2、两个链表合并

如何将两个带有尾指针的链表合并

思路分析

image-20211104202129448

操作分析

 //用p存表头结点p = Ta->next;//Tb表头连接Ta表尾Ta -> next = Tb ->next ->next; //释放Tb表头结点delete Tb->next;//修改指针Tb -> next = p;

具体代码

LinkList Counnect(LinkList Ta,LinkList Tb){//假设Ta、Tb都是非空的单循环链表p = Ta->nextl//p存表头结点Ta->next = Tb->next->next;//Tb连Ta表尾delete Tb->next;//释放Tb表头结点Tb->next = p;/return Tb;
}

4.3 静态链表

在考研中极少考察静态链表的代码实现。所以考研人可以跳步地观看这一小节。

在早期的编程环境中,并没有像C语言一样这么高级的指针机制,如果失去了指针这一工具,我们前面讲的链表结构就失效了。为此,人们想出了用数组来代替指针来描述单链表。

我们让数组的元素由两个数据域组成,data和cur。也就是说,数组的索引下标对应一个data和一个cur。数据域data用来存放数据元素,而游标cur相当于单链表中的next指针,存放该元素的后继在数组中的下标。如果用一幅图描述如下所示:

image-20220214113752557

我们把这种用数组描述的链表叫做静态链表,有的书上也叫做游标实现法

4.3.1 静态链表的定义

对于静态链表来说,由于其实际上是用数组去存放的,所以需要一整片连续的空间,存储密度高;并且由于数组具有静态的特点,所以为了方便插入数据,我们通常会把数组建立得大一些,以便插入空闲空间可以方便插入时不至于溢出。

#define MAXSIZE 100
typedef struct
{ElemType data;int cur
}Component,StaticLinkList [MAXSIZE]; 

4.3.2 静态链表的初始化

在单链表的初始化中,我们是将头结点的指针域置空。对应到静态链表来,我们可以将其游标设为-1,表示数组中没有索引可以给其指向。

Status InitList(StaticLinkList space)
{int i;for(i = 0;i<MAXSIZE-1;i++)space[i].cur = -2;space[MAXSIZE-1].cur = 0;return OK;
}

针对上面的代码实际上我是做了部分改进的,for循环的作用是为数组中每个元素的游标附上一个-2,这是因为内存中本来就含有脏数据,如果我们不对游标做初始化,里面还是有数据,在添加元素的时候就无法判别哪个数组元素是空位置。当然如果你愿意,-2也可以是其他负数或是一些特定的数字。

4.3.3 静态链表的插入

对于静态链表来说,由于其不像单链表一样可以生成结点,也没有可以释放结点的功能,所以这两个功能我们必须自己实现。

对于插入位序为i的结点来说,我们可以分以下步骤进行:

  • 找到一个空的结点,存入数据元素
  • 从头结点除法找到位序为i-1的结点
  • 修改新结点的next
  • 修改i-1号结点的next

4.3.4 静态链表的删除

如果想要删除一个节点,可以分以下步骤进行:

  • 从头结点出发找到前驱结点
  • 修改前驱结点的游标
  • 被删除结点的next设为-2(设为初始化值即可)

4.3.5 静态链表后话

静态链表的优点和缺点都显而易见。优点是在插入和删除操作时,只需要修改游标,不需要移动元素,从而改进了顺序存储结构中插入和删除操作需要移动大量元素的缺点。

而其缺点是:其虽然是顺序存储,却硬要实现链表,这就导致了其失去了随机存取的特性,而且由于数组长度固定不变,导致表长不能扩展。(虽然可以用动态数组,但那也太麻烦了)

总的来说,静态链表还是在一些地方适用的,比如在一些不支持指针的低级语言和一些数组元素数量固定不变的场景(如操作系统的文件分配表FAT)还是比较适用的。

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

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

相关文章

量子计算机怎么储存,什么是量子计算机_量子计算机原理_量子计算的两种有效方法...

量子计算机是一种可以实现量子计算的机器&#xff0c;是一种通过量子力学规律以实现数学和逻辑运算&#xff0c;处理和储存信息能力的系统。它以量子态为记忆单元和信息储存形式&#xff0c;以量子动力学演化为信息传递与加工基础的量子通讯与量子计算&#xff0c;在量子计算机…

Spring注解依赖注入的三种方式的优缺点以及优先选择

当我们在使用依赖注入的时候&#xff0c;通常有三种方式&#xff1a; 1.通过构造器来注入&#xff1b; 2.通过setter方法来注入&#xff1b; 3.通过filed变量来注入&#xff1b; 那么他们有什么区别吗&#xff1f;应该选择哪种方式更好&#xff1f; 代码示例&#xff1a; Const…

seo超强外部链接、内部链接技巧

轮链-混链对做内外链都是非常有效果的&#xff01; 外连&#xff0c;之于大家并不陌生&#xff0c;增加外链的方法也海了去&#xff0c;如软文推广、博客推广、友情链接等等等等&#xff0c;在这里我就不再累述&#xff0c;最近在一些文章里看到轮链和混链的SEO优化技巧&#x…

王道操作系统考研笔记——1.1.5 中断和异常

如果这篇博客对您有用的话&#xff0c;可以给我点个赞吗&#xff0c;这对我很重要&#xff0c;谢谢&#xff01;❤️ 文章目录1.1.5 中断和异常1.1.5.1 中断机制的诞生1.1.5.2 中断的概念和作用1.1.5.3 中断的分类1.1.5.4 小结1.1.5 中断和异常 知识总览 1.1.5.1 中断机制的诞…

LR+Jenkins实践思路

思路&#xff1a;在Loadrunner的安装bin目录下有Loadrunner Control模块的启动程序 “Wlrun.exe”&#xff0c;想利用Jenkin的 windows的batch命令持续构建一个任务&#xff0c;自动启动运行场景&#xff0c;且把结果发给analysis 通过Batch来调用LR场景&#xff1a; Loadrunne…

王道操作系统考研笔记——2.1.1 进程的定义、组成、组织方式和特征

如果这篇博客对您有用的话&#xff0c;可以给我点个赞吗&#xff0c;这对我很重要&#xff0c;谢谢&#xff01;❤️ 文章目录2.1.1 进程的定义、组成、组织方式和特征2.1.1.1 进程的定义2.1.1.2 进程的定义2.1.1.3 进程的组成2.1.1.4 进程的组织2.1.1.5 链接方式2.1.1.6 索引方…

服务器换账号登陆不了怎么办,怎么切换账号 更换账号 注册新的账号小技巧分享给你...

很多玩家喜欢多尝试几种玩法&#xff0c;那么万国觉醒怎么切换账号&#xff1f;切换账号后玩家如何再重新登入游戏&#xff1f;如何在多个账号中快速切换&#xff1f;今天小编就跟大家说说万国觉醒快速切换账号登入游戏的技巧。一、切换账号介绍我们常说的切换账号有两种意思&a…

vue基本介绍

https://cn.vuejs.org/v2/guide/ Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第三方库或既…

android控件的隐藏与显示

2019独角兽企业重金招聘Python工程师标准>>> visibility&#xff1a;显示 invisibility&#xff1a;不显示&#xff0c;不可见但是仍然占据空间 gone&#xff1a;不显示&#xff0c;不可见不占用空间 view.setVisibility();----设置值&#xff1a;0代表visibility&a…

《BI那点儿事》Microsoft 线性回归算法

Microsoft 线性回归算法是 Microsoft 决策树算法的一种变体&#xff0c;有助于计算依赖变量和独立变量之间的线性关系&#xff0c;然后使用该关系进行预测。该关系采用的表示形式是最能代表数据序列的线的公式。例如&#xff0c;以下关系图中的线是数据最可能的线性表示形式。 …

王道操作系统考研笔记——2.1.2 进程的状态和转换

如果这篇博客对您有用的话&#xff0c;可以给我点个赞吗&#xff0c;这对我很重要&#xff0c;谢谢&#xff01;❤️ 2.1.2 进程的状态和转换 知识总览 2.1.2.1 三种基本状态 进程是程序的一次执行。在这个执行过程中&#xff0c;有时进程正在被CPU处理&#xff0c;有时又需要…

Java中抽象类和接口在概念、语法和应用上的区别和关系

2019独角兽企业重金招聘Python工程师标准>>> 春招开始了&#xff0c;盆友们都忙着准备笔试、准备面试&#xff0c;复习学过的知识点&#xff0c;当然我也不例外&#xff0c;在这里祝每一个"有心人"心想事成&#xff0c;梦圆2016&#xff0c;加油&#xff…

王道操作系统考研笔记——2.1.3 进程控制

如果这篇博客对您有用的话&#xff0c;可以给我点个赞吗&#xff0c;这对我很重要&#xff0c;谢谢&#xff01;❤️ 2.1.3 进程控制 知识总览 2.1.3.1 进程控制的过程 进程控制的主要功能是对系统中的所有进程实施有效的管理&#xff0c;它具有创建新进程、撤销已有进程、实…

王道操作系统考研笔记——2.1.4 进程通信

如果这篇博客对您有用的话&#xff0c;可以给我点个赞吗&#xff0c;这对我很重要&#xff0c;谢谢&#xff01;❤️ 2.1.4 进程通信 知识总览 2.1.4.1 什么是进程通信 顾名思义&#xff0c;进程通信就是指进程之间的信息交换。 进程是分配系统资源的单位&#xff08;包括内…

关于设计模式——策略模式-Strategy Pattern

文章目录1 策略模式1.1 模拟鸭子1.2 设计原则1.3 整合行为1.4 模拟鸭子代码的代码1.5 动态设定行为1.6 重新查看整体1.7 继承和组合1.8 总结1.9 优劣期间应用场景2.0 参照资料1 策略模式 在我们什么都不会的情况下&#xff0c;我们先无需知道什么是策略模式&#xff0c;我们要…

python (3):wxPython打包app,报错

1&#xff0c;打包app报错 如图&#xff1a; 使用py2app&#xff0c;mac下打包成app。异常。程序直接退出。 没有详细的错误信息&#xff0c;client程序直接崩溃了。 2。原因 代码没有几行&#xff1a; #!/usr/bin/python # -*- coding: utf-8 -*- import wxapp wx.App(False…

.NET Framework 工具

您可以使用 .NET Framework 工具轻松创建、部署和管理面向 .NET Framework 的应用程序和组件。 此节中介绍的大部分 .NET Framework 工具将自动随 Visual Studio 一起安装。 &#xff08;有关安装信息&#xff0c;请参阅 Visual Studio 下载。&#xff09; 可以从命令行运行除程…

并发无锁队列学习(概念介绍)

1、前言 队列在计算机中很重要的一种数据结构&#xff0c;尤其在操作系统中。队列典型的特征是先进先出&#xff08;FIFO&#xff09;&#xff0c;符合流水线业务流程。在进程间通信、网络通信之间常常採用队列做缓存&#xff0c;缓解数据处理压力。结合自己在工作中遇到的队列…

王道操作系统考研笔记——2.1.5 线程概念与多线程模型

文章目录2.1.5 线程概念与多线程模型2.1.5.1 线程的来源2.1.5.2 线程机制带来的变化2.1.5.3 线程的属性2.1.5.4 线程的实现方式2.1.5.5 多线程模型2.1.5.6 小结2.1.5 线程概念与多线程模型 2.1.5.1 线程的来源 在很久以前还没有引入进程之前&#xff0c;系统中的各个程序只能…

mybatis处理集合、循环、数组和in等语句的使用

2019独角兽企业重金招聘Python工程师标准>>> 在Mybatis的xml配置中使用集合&#xff0c;主要是用到了foreach动态语句。 foreach的参数&#xff1a;foreach元素的属性主要有 item&#xff0c;index&#xff0c;collection&#xff0c;open&#xff0c;separator&…