数据结构笔记 -01线性表

线性表的定义

结构:

1. 线性结构: 结构中的数据元素之间均满足线性关系(一对一)。

数据结构笔记 -01线性表_第1张图片
图片.png

2. 由若干个数据项组成的数据元素,称为记录;含有大量记录的线性表称为文件;同一线性表中的元素必须属于同一对象。
3. 线性表:n个数据元素的有限序列 数据元素/结点:原子类型或结构类型 线性表长度:线性表中元素个数n 位序:各线性元素都有一个确定的位置,即位序
非空线性表通常记作:(a1,a2,……a i,a i+1,……a n),a1:开始结点、表头结点,a n:终止结点、表尾节点

抽象数据操作:

1. 线性表的抽象数据类型定义-创建删除:

数据结构笔记 -01线性表_第2张图片
图片.png

DestroyList(&L):释放掉表中的存储空间;ClearList(&L):将线性表恢复到空表状态,未释放存储空间。
2. 线性表的抽象数据类型定义-查找:
数据结构笔记 -01线性表_第3张图片
图片.png

ps:LocateElem:用指针的目的是提高操作的通用性
3. 组合的复杂操作:
①两线性表的合并
数据结构笔记 -01线性表_第4张图片
图片.png

数据结构笔记 -01线性表_第5张图片
图片.png

当找到与e相同的,则返回满足equal()关系的元素的位序,否则返回0。
算法实现:
数据结构笔记 -01线性表_第6张图片
图片.png

②归并有序线性表
数据结构笔记 -01线性表_第7张图片
图片.png

算法思路:先比较A、B两线性表中的第一个元素,将较小的那一个取出放入新线性表C;再比较A、B两线性表的元素,将较小的那一个取出放入新线性表C;重复此操作,直至A或者B的元素个数为0了,将剩下的表里面的元素全部插至C的末尾。
数据结构笔记 -01线性表_第8张图片
图片.png

算法实现:
数据结构笔记 -01线性表_第9张图片
图片.png

ps:线性表中的元素有序排列,可提高归并算法的时间效率。

顺序表

顺序表的存储结构

1. 线性表的顺序表示:把线性表的结点按逻辑顺序依次存放在一组地址连续的存储单元里。
2. 存储结构:只要知道了线性表中第一个元素的存储位置(基地址),那么线性表中任意一个元素,如第i个元素即可根据式子计算。

数据结构笔记 -01线性表_第10张图片
图片.png

3. 线性表的动态分配线性存储:顺序表SqList类型是结构体类型,由三个域组成。Elem指针域用来指示顺序表的基地址;length域用来存储当前表长;listsize用来存储线性表当前最大存储容量。(length域和listsize以元素个数为单位)
数据结构笔记 -01线性表_第11张图片
图片.png

顺序表基本操作的实现

1. 创建空线性表
算法思路:先分配一块存储空间;让Elem指针域指向它的基地址;将length域的值置为0

数据结构笔记 -01线性表_第12张图片
图片.png

算法实现:
数据结构笔记 -01线性表_第13张图片
图片.png

函数返回值Status类型是Int类型的别名,可以用OVERFLOW、OK等常量来代表函数执行结果的不同状态
2. 顺序表的插入
ListInsert将在第i个元素和第i-1个元素之间插入新元素;为防止元素被覆盖,元素搬动顺序应为由后往前搬动,如图。
数据结构笔记 -01线性表_第14张图片
图片.png

注意:
数据结构笔记 -01线性表_第15张图片
图片.png

算法实现:
realloc:按新的容量分配新的存储空间,并将原来空间里的数据拷贝到新的空间里,释放掉原来的空间。
若空间分配成功,相应地修改基地址指针域L.elem和存储容量域L.listsize
数据结构笔记 -01线性表_第16张图片
图片.png

3. 顺序表的删除
数据结构笔记 -01线性表_第17张图片
图片.png

注意:
数据结构笔记 -01线性表_第18张图片
图片.png

算法实现:
数据结构笔记 -01线性表_第19张图片
图片.png

4. 插入和删除操作的算法分析
最好和最坏情况下时间复杂度的分析:
数据结构笔记 -01线性表_第20张图片
图片.png

平均情况下移动元素次数的期望:
数据结构笔记 -01线性表_第21张图片
图片.png

总结:顺序表的插入和删除操作的时间复杂度都是O(n);缺点:在平均情况下,需要移动的表中元素为表中元素的一半,当顺序表长n很大时,耗费较大。
5. 顺序表的查找
按位序查找(GetElem):时间复杂度O(1)
按内容查找(LocateElem):从顺序表的第一个元素逐一比较
数据结构笔记 -01线性表_第22张图片
图片.png

ps:*(p++)指先比较p指向的元素和e的大小,再将p++;时间复杂度为O(n)。
6. 归并有序的顺序表
算法实现:
数据结构笔记 -01线性表_第23张图片
图片.png

ps:加入红色部分则实现并集操作
7. 顺序表的优缺点
数据结构笔记 -01线性表_第24张图片
图片.png

线性链表

线性表(链表)的链式存储

1. 链表的链式存储结构: 用一组任意的(可连续、也可不连续)存储单元来存储线性表的数据元素。
节点包含两种信息域:1)数据域:存储数据元素的信息 2)指针域:存储数据间的逻辑链接信息
2. 分类
按连接方式分类

  • 线性链表(单链表)
  • 循环链表
  • 双向链表
    按实现方式分类
  • 静态链表
  • 动态链表

线性链表

线性链表的动态链式存储:单链表

1. 结构特点: 链表中每个节点只包含一个用来指示直接后继的指针域。(如老鹰捉小鸡的小鸡)
2. 头指针: 用来指示链表第一个结点的存储位置

  1. 最后一个结点的指针域为NULL


    数据结构笔记 -01线性表_第25张图片
    图片.png

    数据结构笔记 -01线性表_第26张图片
    图片.png
  2. 假设L为单链表的头指针,则L应为LinkList型变量。当L=NULL时,该链表为空表
  3. 头结点:在表的第一个结点之前附设一个结点,其next域指向第一个元素结点,其data域可以不储存任何信息,也可以用于储存表长等附加信息。带头结点的单链表中,当头结点的next域为空时,该链表为空表
  4. 带头结点vs不带头结点:
  • 不带头结点的单链表:执行添加操作时,若添加的结点在第一个节点之前,则需要修改头指针;执行删除操作时亦是如此。
  • 带头结点的单链表
    ①单链表的查找(按位序查找):在单链表中,要读取第i个数据元素,必须从头指针L出发,先找到表中的第一个结点;再顺藤摸瓜,依次查找。
    具体操作:
    数据结构笔记 -01线性表_第27张图片
    图片.png
    该操作的时间复杂度为O(n),在顺序表中只需要O(1)。
    ②单链表的插入:如果要将新元素e插入到第i个元素之前,则首先需要找到表中的第i-1个结点,然后把辅助指针p指在第i-1个结点上;若需要插入在第一个结点之前,则只需把p指在头结点上。
    具体操作:动态分配一个新节点,用s指针指向这个新结点;将新元素e写入这个结点;修改新结点的next指针域,让它指向表中第i个结点;修改p的next指针域,使它指向新插入的那个结点。
    数据结构笔记 -01线性表_第28张图片
    图片.png
    顺序不能替换
    语言描述:
    数据结构笔记 -01线性表_第29张图片
    图片.png

    ③单链表的删除:如果要删除第i个元素,则先将辅助指针p定位到第i-1个结点上,即被删除结点的直接前驱;此外,还需要引入一个辅助指针q用来指向被删结点;然后,修改第i-1结点的next指针,使其指向被删结点的直接后继;再将被删结点的值赋值给引用参数e;最后,释放掉被删结点,即q所指的结点。
    数据结构笔记 -01线性表_第30张图片
    图片.png

    ?是否可以不引入辅助指针q:若不引入,则被删结点无法被free掉,它将成为系统中的太空垃圾,一直占用着这部分的存储空间,直到程序运行结束才会被释放,可能造成内存泄露的隐患。
    具体操作:
    数据结构笔记 -01线性表_第31张图片
    图片.png

    ④单链表的创建:链式存储结构与顺序存储结构的最大区别在于,它是动态结构,链表所需的空间不必提前估算并分配,只需要按需实时动态分配或释放即可。因此,建立单链表的过程即是从空链表开始,依次生成各个结点,并逐个插入到链表的过程。
    具体操作:
    (1)该算法建立的是从表尾到表头逆向建立单链表
    数据结构笔记 -01线性表_第32张图片
    图片.png

    (2)建立的是从表头到表尾正向建立单链表
    首先,增设一个表尾指针T,初始令其指向头结点;其次,将插入的位置改为插入到表尾节点之后;在每次插入后,修改T指针,使其每次指向新插入的那个结点。
    数据结构笔记 -01线性表_第33张图片
    图片.png

    ⑤归并俩有序单链表:拆东墙补西墙,没有分配任何其他的结点空间,而是将原来的单链表重新分解开,按照数值大小连接成新的单链表。
    数据结构笔记 -01线性表_第34张图片
    图片.png
线性链表的静态链式存储:静态链表

1. 定义: 在实际开发中,可能会遇到不方便使用指针或动态分配的情况,比如没有提供指针类型的编程语言,此时,可以采用一维数组来描述链表,这种实现的方法称为静态链表。
2. 结构特点: 预先分配一个较大的数组空间,数组中的元素有俩域,data域用来存储结点的数据,cur域称为游标或指示器,用来存储后继结点在数组中的下标。

数据结构笔记 -01线性表_第35张图片
图片.png

游标cur用结点在数组中的相对位置来代替结点在内存中的绝对位置(即指针),因此,静态链表中的元素存储位置可以是任意的,插入和删除时不需要移动元素,只需要修改游标;但缺点是,它与顺序表一样,需要提前分配一块存储空间,闲置的结点空间会杂乱地分布在数组中。
为了再利用这些空闲空间,可以利用cur域,将这些空闲的单元链接成一条备用链。
图片.png

因此,整个存储空间中有两条链表,一条是用来存储线性表的静态链表,另一条是空闲结点构成的备用链表。可以约定将数组下标为0的单元用作备用链表的头结点,另外设置变量S来存储链表的头指针。可以用cur域的值为0来表示该结点是表尾节点。
3. 带头结点的静态链表的操作
①按内容查找:根据cur域在表中顺链查找
数据结构笔记 -01线性表_第36张图片
图片.png

②初始创建静态链表:以space[0]作为头结点
数据结构笔记 -01线性表_第37张图片
图片.png

③插入结点时:若备用链表第一个结点非空,则删除备用链表的第一个结点,将其下标返回给插入算法,并将其作为待插入的新结点;反之,若备用链表已空(space[0].cur==0),则返回的是0,表示分配空间失败。
数据结构笔记 -01线性表_第38张图片
图片.png

④静态表中free的实现,即就是将被删掉的结点回收入备用链表:将被删结点插回备用链表表头,即插入在头结点space[0]之后。
数据结构笔记 -01线性表_第39张图片
图片.png

4. 静态链表的优缺点
兼具顺序存储和链式存储的部分特点,主要为不支持指针类型的编程语言提供了实现线性链表的方法。

  • 优点:插入和删除操作时,只需要修改游标,不需要移动元素。
  • 缺点:和顺序表一样,需要提前分配大块连续存储空间,无法解决难以确定合适存储规模的问题;失去顺序存储结构随机存取的优势,只能顺序存取。
实例

1. 集合运算的实现

数据结构笔记 -01线性表_第40张图片
图片.png

思路:1)先依次输入集合A中的各个元素,正序建立表示集合A 的静态链表S,并且用辅助指针r指向集合A中的最后一个结点;而后,每输入一个集合B的元素,b j+1(j的取值从0开始)。 2)在链表S中进行查找,最多查找到指针为r的结点即可。 3)如果存在与b j+1相同的结点,则从S中删除这个结点,否则,就把b j+1插入到r所指的结点之后。(集合B逆序排列在r所指结点之后)
数据结构笔记 -01线性表_第41张图片
图片.png

数据结构笔记 -01线性表_第42张图片
图片.png

数据结构笔记 -01线性表_第43张图片
图片.png

循环链表和双向链表

循环链表

1. 导言: 在实际生活中,我们往往需要遍历线性表,即需要逐一对表中的某结点做一些操作。
若我们需要按一定规律,逐一对线性表中的各个结点进行某种操作(称为访问),使得某个结点均被且只被访问一次。而该种操作用单链表实现,则只能从头指针开始。
2. 循环链表定义: 表中最后一个结点的指针域指向头结点,整个链表形成一个环。
3. 特点: 从表中的任意一个结点出发,都能遍历所有结点。与线性链表操作基本一致,差别在于算法中的循环终止条件不是“p或p->next为空”,而是他们是否等于头指针或起点。
4. 与单链表的区别:
在单链表中,如果只设置头指针,访问表头结点,仅需要O(1)时间,但访问表尾结点,则需要O(n)时间,若要提高表尾结点的访问效率,就得增设尾指针。
而对于循环链表中,设置尾指针而不设置头指针,就可以做到访问表头和表尾结点都只需要O(1)时间。
5. 两线性链表的头尾相接(合并): 当需要做到两线性链表头尾相接,合并成一个表时,采用循环链表结构,只需要修改两个指针值,仅需要O(1)时间。
具体操作:1)先设置两个辅助指针p和q,分别指向俩循环链表的头结点; 2)修改表A的表尾结点指针,使其指向表B的表头结点; 3)修改表B的表尾结点指针,使其指向表A的头结点; 4)释放掉表B的头结点。

数据结构笔记 -01线性表_第44张图片
图片.png

双向链表

1. 导言: 由于单链表只有一个指向后继结点的指针的特点,若用单链表找直接后继仅需O(1)的时间,但要找直接前驱,则需O(n)的时间。为解决这个麻烦,诞生了双向链表,在双向链表中,NextElem和PriorElem都仅需O(1)时间。
2. 双向链表定义: 链表的每个结点有两个指针域,一个指向直接后继,另一个指向直接前驱。(即小盆友手牵手排成一列)
3. 类型定义: 在单链表的基础上多了一个用来指向直接前驱的prior指针域。
4. 存储结构:

数据结构笔记 -01线性表_第45张图片
图片.png

5. 带头结点的双向循环链表:
数据结构笔记 -01线性表_第46张图片
图片.png

这样,循环链表就同时具有了两个方向的环路。当双向循环链表为空时,只有一个头结点,且头结点的next和prior都指向自己。
6. 带头结点的双向循环链表的操作
①ListLength、GetElem和LocateElem只需要涉及一个方向的指针,和单链表一致。
②插入:将指针s所指的新结点插入到p指向的结点之前,无需引入其他的指针。
具体操作:1)令要插入的那个新结点的s指针的前驱指向p指向结点的前驱 2)令p指向的结点的前驱结点的next指针指向新结点 3)令新结点的next指针指向p所指的结点 4)令p所指向结点的prior指针指向新结点
数据结构笔记 -01线性表_第47张图片
图片.png

?这四个步骤的顺序可以任意调换吗? 答:步骤四不能早于步骤一
具体算法实现:
数据结构笔记 -01线性表_第48张图片
图片.png

③删除:删除p指向的结点,也无需引入辅助指针。
具体操作:1)使目标结点的前驱结点的next指针指向目标结点的后继 2)令目标结点的后继结点的prior指针指向目标结点的前驱 3)释放掉目标删除结点
数据结构笔记 -01线性表_第49张图片
图片.png

顺序可对换
具体算法实现:
数据结构笔记 -01线性表_第50张图片
图片.png

顺序表与链表的比较

(图片为网络课程自截,侵删)

你可能感兴趣的:(数据结构笔记 -01线性表)