• 数据结构_第二章 线性表


    第二章 线性表

    文章目录

    2.1线性表及抽象数据类型

    线性表

    定义
    线性表(Linear List)是由n(n≥0)个类型相同的数据元素a 1,a 2,..,a,组成的有限序列,记做(a 1,a 2...a i-1,a i, a i+1...., an) .
    数据元素之间是一对一的关系,即每个数据元素最多有一个直接前驱和一个直接后继。
    逻辑结构图: o—o—o—o—o—o
    

    在这里插入图片描述

    特点
    (1)同一性:线性表由同类数据元素组成,每一个a必须属于同一数据对象。
    (2)有穷性:线性表由有限个数据元素组成,表长度就是表中数据元素的个数。
    (3)有序性:线性表中相邻数据元素之间存在着序偶关系
         

    抽象数据类型

    定义
    抽象数据类型定义
    ADT LinearList{
        数据元素:D={ai|ai∈Do,i=1,2,...,n;n≥0,Do为某一数据对象}
        关系:S=|ai,ai+1∈Do,i=1,2,...n-1}
        基本操作:
           ...        
    }ADT LinearList
    

    2.2线性表的顺序存储结构

    定义
    线性表顺数存储结构:是指用一组地址连续的存储单元依次存储线性表中的各个元素,使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中。
    采用顺序存储结构的线性表通常称为顺序表(SequentialList)假设线性表中每个元素占k个单元,第一个元素的地址为loc(a),称为基地址,则第i个元素的地址为:loc(ai)=loc(ai)+(i-1)×k
    
    C语言定义
    #define maxsize 100产/*线性表可能达到的最大长度*/
    typedef struct
    {
        ElemType elem[maxsize]; /*线性表占用的数组空间*/
        int last;/*表尾指示器,记录线性表中最后一个元素在数组elem[]中的位置(下标值)*/
    }SeqList;
    
    注意:表的长度length为last+1.
    
    三个属性
      (1)存储空间起始位置,数组data:它的存储位置就是存储空间的存储位置
      (2)最大存储容量:数组长度MaxSize
      (3)线性表当前长度:last+1 (length)
    
      在任意时刻,线性表长度<=MaxSize(线性表长度是指线性表中数据元素的个数,随着线  性表插入和删除操作的进行,这个量是变化的)
    
    基本运算(查找、插入、删除)
    (1)查找操作

    在这里插入图片描述

    (2)插入操作

    在这里插入图片描述

    (3)删除操作

    在这里插入图片描述

    (4)总结
    查找、插入、删除算法的平均时间复杂度为:O(n)
    
    顺序表的空间复杂度S(n)=o(1)(没有占用辅助空间)
    
    线性表的顺序存储结构特点、优点及缺点
    特点
    (1)利用数据元素的存储位置表示线性表中相邻数据元素之间的前后关系,即线性表的逻辑结构与存储结构一致
    (2)在访问线性表时,可以快速地计算出任何一个数据元素的存储地址。因此可以粗略地认为,访问每个元素所花时间相等,这种存取元素的方法被称为随机存取法
    (3)存储密度大(结点本身所占存储量/结点结构所占存储量)
    
    优点
    (1)用数组存储数据元素,操作方法简单,容易实现
    (2)无须为表示结点间的逻辑关系而增加额外的存储空间
    (3)存储密度高
    (4)可方便地随机存取表中的任一元素。
    
    缺点
    (1)插入或删除运算不方便,除表尾的位置外,在表的其它位置上进行插入或删除操作都必须移动大量的结点,其效率较低;
    (2)由于顺序表要求占用连续的存储空间,存储分配只能预先进行静态分配。因此当表长变化较大时,难以确定合适的存储规模。(浪费存储空间,数据元素的个数不能自由扩充)
    
    为克服这一缺点-->链表!!!
    

    2.3线性表的链式存储结构–单链表

    单链表

    单链表定义
     链表中的每个结点的指针域只有一个
    
    结点(Node)
    为了正确的表示结点间的逻辑关系,必须在存储线性表的每个数据元素值的同时,存储指示其后继结点的地址(或位置)信息,这两部分信息组成的存储映像叫做结点。
    
    单链表包括两个域
    (1)数据域:用来存储结点的数据值
    
    (2)指针域:用来存储数据元素的直接后继的地址(或位置)
    
    头指针
    指向链表的第一个结点的指针。
    
    线性表中的第一个结点无前驱,设一个头指针指向第一个结点;
    线性表的最后一个结点无直接后继,则指定单链表的最后一个结点的指针域为“空”(NULL)。
    

    在这里插入图片描述

    头结点
    有时为了操作方便,在单链表的第一个结点之前附加一个结点,称为头结点。头结点的数据域可以存放标题、表长等信息,也可以不存储任何信息,其指针域存储第一个结点的地址,这时头指针不再指向表中第一个结点而是指向头结点。
    
    头指针与头结点对比

    在这里插入图片描述

    线性表链式存储结构

    带头结点的空单链表

    带头结点的单链表的判空条件:L->next==NULL
    
    不带头结点的单链表的判空条件:L==NULL
    

    在这里插入图片描述

    在这里插入图片描述

    链表的特点
    1、结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不—定相邻
    2、访问时只能通过头指针进入链表,并通过每个结点的指针域向后扫描其余结点,所以寻找第一个结点和最后一个结点所花费的时间不等
    这种存取元素的方法被称为顺序存取法
    
    链表的优缺点
    优点:
    1、数据元素的个数可以自由扩充
    2、插入、到除等操作不必移动数据,只需修改链接指针,修改效率较高
    
    缺点:
    1、存储密度小
    2、存取效率不高,必须采用顺序存取,即存取数据元素时,只能按链表的顺序进行访问(顺藤摸瓜)
    

    单链表上的基本运算

    存储空间的分配和释放
    需要调用C语言的标准函数malloc函数和free函数来实现结点空间的获取和释放
    
    P=(LinkList) malloc (sizeof(Node))
    表示申请一块Node类型的存储单元空间,并将其转换为LnkList类型赋给变量p
    
    free ( p)
    表标释放指针p所指向的结点空间
    
    (1)建立单链表
    头插法

    在这里插入图片描述

    尾插法

    在这里插入图片描述

    总结
    头插法:在链表的头部插入结点建立单链表,数据读入顺字和线性表中的逻辑顶序正好相反;
    
    尾插法:在链表的尾部插入结点建立单链表。数据读入顺序和线性表中的逻辑顺序正好相同;
    
    两者的算法时间复杂度为O(n)
    
    (2)单链表查找
    按序号查找

    ​ 已知带头结点的单链表L,要查找表中第i个结点,则需要从单链表的头指针L出发,从第一个结点(L->next)开始顺着链域扫描。

    ​ 用指针p指向当前扫描到的结点初值指向第一个结点(p=L->next) ,用j做记数器,累计当前扫描过的结点数(初值为0),当j=i时,指针p所指的结点就是要找的第i个结点。

    在这里插入图片描述

    按值查找

    ​ 指在单链表中查找是否有结点值等于e的结点,若有的话,则返回首次找到的其值为e的结点的存储位置,否则返回NULL。
    ​ 查找过程从单链表的头指针指向的第一个结点出发,顺着链逐个将结点的值和给定值e作比较。

    在这里插入图片描述

    (3)单链表插入

    ​ 要在带头结点的单链表L中第i个数据元素之前插入一个数据元素e,需要首先在单链表中找到第i-1个结点并由指针pre指示,然后申请一个新的结点并由指针s指示,其数据域的值为e,并让pre的后继结点改成s的后继结点,再把结点s变成pre的后继结点。

    插入算法步骤:
    1、找到ai-1存储位置p
    2、生成一个新结点*s
    3、将新结点*s的数据域置为x
    4、新结点*s的指针域指向结点ai*
    5、令结点*p的指针域指向新结点*s
    

    在这里插入图片描述

    在这里插入图片描述

    (4)单链表删除

    ​ 在带头结点的单链表L中删除第i个结点,则首先要通过计数方式找到第i-1个结点并使pre指向第i-1个结点,而后删除第i个结点并释放结点空间。

    删除算法步骤:
    1、找到ai-1,存储位置p
    2、保存要删除的结点的值
    3、令p->next指向a,的直接后继结点
    4、释放结点a的空间
    

    在这里插入图片描述

    在这里插入图片描述

    (5)求单链表的长度
    “数”结点:
    指针p依次指向各个结点
    从第一个元素开始“数”
    一直“数”到最后一个结点(p->next=NULL)
    
    注:单链表的存储密度小于1
    

    在这里插入图片描述

    在这里插入图片描述

    2.4线性表的链式存储结构–循环链表

    循环链表定义
    循环链表(Circular Linked List):是一个首尾相接的链表。
    特点:将单链表最后一个结点的指针域由NULL改为指向头结点或线性表中的第一个结点,就得到了单链形式的循环链表,并称为循环单链表。在循环单链表中,表中所有结点被链在一个环上。
    

    在这里插入图片描述

    循环链表–算法实现1

    在这里插入图片描述

    循环链表–算法实现2

    在这里插入图片描述

    循环链表的特点
    1、从表中任一结点出发都能访问到表中所有结点;
    2、循环表是对称的,为了判断起始位置,一般要设置头结点。头结点的设置也可将空表和非空表的逻辑状态及运算统一起来
    3、循环链表的运算和线性链表基本一致,有时可简化某些运算。
    4、若对单链表的常用操作是在表头和表尾进行的,可以采用带尾指针的循环单链表。
    

    2.5线性表的链式存储结构–双向链表

    双向链表定义
    定义:在单链表的每个结点里再增加一个指向其前趋的指针
    域prior。这样形成的链表中就有两条方向不同的链,我们称之为双(向)链表。
    结构定义: typedef struct Dnode
             {
    			ElemType data;
    			struct DNode *prior,*next;
    	     }DNode,*DoubleList;
       
       prior | data | next    找前驱结点复杂度O(1)
    

    在这里插入图片描述

    前插操作

    在这里插入图片描述

    后插操作

    在这里插入图片描述

    删除操作

    在这里插入图片描述

    在这里插入图片描述

    线性表链式存储方式的比较

    在这里插入图片描述

    2.6线性表的链式存储结构–静态链表

    静态链表定义
    静态链表借助数组来描述线性表的链式存储结构,结点也有两个域:data域和cursor域, data域存放结点的数据信息, cursor域存放的不再是指针,而是游标,存放的是其后继结点在结构数组中的相对位置数组下标值)。数组的第0个分量可以设计成表的头结点,头结点的cursor域指示了表中第一个结点的位置;表尾结点的cursor域为-1,表示静态单链表的结束。
    和顺序表一样,静态链表也要预先分配一块连续的内存空间。
    
    静态链表存储示意图

    在这里插入图片描述

    静态链表结构类型描述
    #define Maxsize 50
    typedef struct
    {
      ElemType data;
      int cursor;
    }StaticList[Maxsize]
    静态链表以cursor==-1作为其结束的标志。静态链表的插入和删除操作与动态链表相同,只需要修改指针,不需要移动元素;总体来讲它虽没单链表使用方便,但在伙些不支持指针的高级语言中,也是一种非常巧妙的设法。
    

    2.7线性表的应用 一元多项式的表示与相加

    顺序存储结构

    表示方式:一个一元多项式pn(X)可按升幂的形式写成:

    在这里插入图片描述

    可以用线性表存储:(p0,p1,p2,p3…pn)

    顺序存储结构适合于非零系数多的多项式。

    在这里插入图片描述

    链式存储结构

    链式存储结构:可用单链表存储多项式的结点结构,只存非零项,每一项有两部分构成(系数项和指数项)

    在这里插入图片描述

    两个多项式相加
    运算规则
    两个多项式中所有指数相同的项的对应系数相加,若和不为零,则构成“和多项式”中的一项;所有指数不相同的项均复抄到“和多项式”中。
    
    (1)若p->expexp,则结点p所指的结点应是“和多项式”中的一项,令指针p后移;
    (2)若p->exp=q->exp,则将两个结点中的系数相加,当和不为零时修改结点p的系数域,释放q结点;若和为零,则和多项式中无此项,删去p和q结点,同时释放p和q结点;
    (3)若p->exp>q->exp,则结点q所指的结点应是“和多项式”中的一项,将结点q插入在结点p之前,且令指针q在原来的链表上后移。
    
    算法实现

    在这里插入图片描述

    用尾插法建立一元多项式

    在这里插入图片描述

    2.8 顺序表与链表的比较

    基于空间的考虑

    顺序表为静态分配的存储结构,故有如下问题:

    (1)要按最大可能的表长分配存储空间;
    (2)必要时难于扩充存储空间。
    

    动态链表的特点

    在于可动态生成,因其需用指针表示元素间的逻辑关系,故其存储密度较低。
    存储密度=元素本身占用存储量/结点占用存储量
    
    基于时间的考虑

    顺序表的特点

    在于可随机存取表中元素,但插入、删除操作时是要大量移动元素;
    

    动态链表的特点

    在于插入、删除操作时只改指针不移动元素,但不能随机存取表中元素,需顺链找;
    
    顺序表和链表的综合比较

    时间性能分析

    查找
    顺序存储结构O(1) ; 
    单链表O(n);
    
    插入和删除
    顺序存储结构需要平均移动表长一半的元素,时间为O(n);
    单链表在计算出某位置的指针后,插入和删除时间仅为O(1);
    
    结论:
    —若线性表需要频繁查找,很少进行插入和删除操作时,宜采用顺序存储结构;
    -若需要频繁插入和删除,宜采用单链表结构;
    -若表的插入和删除操作主要发生在表的首尾两端,宜采用带尾指针的单循环链表
    

    2.9 总结与提高

    线性表的特征
    线性表中每个数据元素有且仅有一个直接前驱和一个直接后继,第一个结点无前驱,最后一个结点无后继。
    
    线性表的存储方式
    线性表顺序存储(顺序表):采用静态分配方式,借助于C语言的数组类型,申请一组连续的地址空间,依次存放表中元素,其逻辑次序隐含在存储顺序之中。
    
    线性表链式存储(链表):采用动态分配方式,借助于C语言的指针类型,动态中请与动态释放地址空间,故链表中的各结点的物理存储可以是不连续的。
    
    单链表的操作特点
    (1)顺链操作技术:从“头”开始,访问单链表L中结点i (p指向该结点〉时,由于第i个结点的地址在第i-1个结点( pre指向该结点,为p的前驱)的指针域中存放,查找必须从单链表的“首结点”开始(p=L)﹔通过p=p->next并辅助计数器来实现;
    (2)指针保留技术:通过对第i个结点进行插入、删除等操作时,需要对第i-1个结点的指针域进行链址操作(pre->next国此在处理过程中始终需要维持当前指针p与其前驱指针pe的关系,将这种技术简称为“指针保留技术”。
    
    链表处理中的相关技术
    (1)单链表与多重链表的差别在于指针域的个数;
    (2)一般链表与循环链表的差别在于是否首尾相接,将非空表.空表等多种情况统一处理,以方便运算。
    (3)判断当前结点p是否为表尾:一般链表中,p结点是表尾的条件是:该结点的后继指针值为空指针,即:p->next= =NULL,循环链表中,p结点是表尾结点的条件是:该结点的后继指针值为头指针值,即:p->next= = head .
    (4)链表的表长度n值并未显式保存:由于链表是动态生成的结构其长度要通过顺链查找到表尾得到。因此在处理链表时,往往是以当前处理位置结点p是否为表尾作为控制条件,而不是以表长度n作为控制条件。
    时,由于第i个结点的地址在第i-1个结点( pre指向该结点,为p的前驱)的指针域中存放,查找必须从单链表的“首结点”开始(p=L)﹔通过p=p->next并辅助计数器来实现;
    (2)指针保留技术:通过对第i个结点进行插入、删除等操作时,需要对第i-1个结点的指针域进行链址操作(pre->next国此在处理过程中始终需要维持当前指针p与其前驱指针pe的关系,将这种技术简称为“指针保留技术”。
    
    链表处理中的相关技术
    (1)单链表与多重链表的差别在于指针域的个数;
    (2)一般链表与循环链表的差别在于是否首尾相接,将非空表.空表等多种情况统一处理,以方便运算。
    (3)判断当前结点p是否为表尾:一般链表中,p结点是表尾的条件是:该结点的后继指针值为空指针,即:p->next= =NULL,循环链表中,p结点是表尾结点的条件是:该结点的后继指针值为头指针值,即:p->next= = head .
    (4)链表的表长度n值并未显式保存:由于链表是动态生成的结构其长度要通过顺链查找到表尾得到。因此在处理链表时,往往是以当前处理位置结点p是否为表尾作为控制条件,而不是以表长度n作为控制条件。
    
  • 相关阅读:
    记录如何用php做一个网站访问计数器的方法
    昇思MindSpore再升级,深度科学计算的极致创新
    【华为OpenEuler】VirtualBox虚拟机与OpenEuler环境搭建教程
    有个开发者总结这 15 优雅的 JavaScript 个技巧
    直播课堂系统10--腾讯云点播管理模块(二)
    【Pytorch】深度学习之数据读取
    Java基础之《HTML5标签(1)》
    联表查询 && 索引 && 事务 && JDBC使用 &&CPU工作原理 && 线程概念 && Thread类的用法
    网络七层协议
    Python的面向对象编程之—— 类和对象
  • 原文地址:https://blog.csdn.net/Angeliaz/article/details/127121875