线性表:零个或多个数据元素的有限序列
同时也是最常用也最简单的一种结构
线性表的定义
- List—->零个或多个数据元素的有限序列
- 线性表元素的个数n(n>=0)定义为线性表的长度,当n=0时,称之为空表
- 在较为复杂的线性表中,一个数据元素可以由若干个数据项组成—->比如大学上课时的花名册,至少有个姓名和学号,姓名和学号就是数据项
- 线性表里的数据元素得是相同类型的数据
线性表的抽象数据类型
抽象数据类型:Abstract Data Type(ADT)
ADT List |
线性表的顺序存储结构
线性表的顺序存储结构,指的是用一段地址连续的存储单元存储线性表的数据元素。
我么通常可以用一维数组来实现顺序存储结构
顺序存储方式
- 存储空间的起始位置:数组data,它的存储位置就是存储空间的存储位置
- 线性表的最大存储容量:数组长度MaxSize
- 线性表的当前长度:length
数据长度与线性表长度区别
数据长度即数组长度——存放线性表的存储空间的长度
线性表的长度:线性表中数据元素的个数
在任意时刻,线性表的长度应该小于等于数组的长度。
存储器中的每个存储单元都有自己的编号,这个编号称为地址。
线性表的第i个元素存储在下标为i-1的位置
顺序存储结构的插入与删除
获得元素操作
typedef int Status; |
插入操作
插入算法的思路:
- 如果插入位置不合理,抛出异常;
- 如果线性表长度大于等于数组长度,则抛出异常或动态增加容量;
- 从最后一个元素开始向前遍历到第i个位置,分别将它们都向后移动一个位置;
- 将要插入元素填入位置i处;表长加1
Status ListInsert(SqList *L,int i,ElemType e) |
删除操作
删除算法的思路:
- 如果删除位置不合理,抛出异常;
- 取出删除元素;
- 从删除元素位置开始遍历到最后一个元素位置,分别将它们都向前移动一个位置;
- 表长减1
Status ListDelete(Sqlist *L,int i,ElemType *e) |
线性表顺序存储结构的优缺点
存,读数据时,不管哪个位置,时间复杂度都是O(1);
而插入或删除时,时间复杂度都是O(n)
这说明:线性表的存储结构比较适合元素个数不太变化,而更多时存取数据的应用。
线性表的链式存储结构
所有元素都不考虑相邻位置,哪有空位就到哪里,只需要让元素知道它下一个元素的位置在哪里就可以了,这样,所有的元素我们都可以通过遍历而找到。
为了表示每个数据元素a[i]与其直接后继数据元素a[i+1]之间的逻辑关系,对数据元素a[i]来说,除了存储其本身的信息之外,还需存储一个指示其直接后继的信息(即直接后继的存储位置),我们把存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称做指针或链。这两部分信息组成数据元素ai的存储映像,称为结点(Node)
单链表的读取
获得链表第i个数据的算法思路:
- 声明一个结点p指向链表第一个结点,初始化j从1开始
- 当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,i累加1
- 若到链表末尾p为空,则说明第i个元素不存在
- 否则查找成功,返回结点p的数据
Status GetElem(Linklist L,int i,ElemType *e) |
核心思想是:工作指针后移
单链表的插入与删除
s->next=p->next;p->next=s; |
单链表第i个数据插入结点的算法思路:
- 声明一结点p指向链表第-一个结点, 初始化j从1开始;
- 当j<i时,就遍历链表,让p的指针向后移动,不断指向下一-结点,j累加1;
- 若到链表末尾p为空,则说明第i个元素不存在;
- 否则查找成功,在系统中生成一一个空结点s;
- 将数据元素e赋值给s->daa;
- 单链表的插入标准语句s->next=p->next;p->next=s;
- 返回成功。
Status ListInsert(LinkList *L,int i,ElemType e) |
单链表的删除
单链表第i个数据删除结点的算法思路:
- 声明一结点p指向链表第一个结点,初始化j从1开始
- 当j<i时,就遍历链表,让p的指针向后移动,不断指向下一个结点,j累加1;
- 若到链表末尾p为空,则说明第i个元素不存在;
- 否则查找成功,将欲删除的结点p->next赋值给q;
- 单链表的删除标准语句p->next=q->next;
- 将q结点中的数据赋值给e,作为返回;
- 释放q结点;
- 返回成功。
Status ListDelete(LinkList *L,int i, ElemType *e) |
对插入或删除数据越频繁的操作,单链表的效率优势就越明显
单链表的整表创建
回顾一下,顺序存储结构的创建,其实就是一个 数组的初始化,即声明一个类型
和大小的数组并赋值的过程。而单链表和顺序存储结构就不一-样,它不像顺序存储结
构这么集中,它可以很散,是一种动态结构。对于每个链表来说,它所占用空间的大
小和位置是不需要预先分配划定的,可以根据系统的情况和实际的需求即时生成。
所以创建单链表的过程就是一个动态生成链表的过程。即从“ 空表”的初始状态
起,依次建立各元素结点,并逐个插入链表。
单链表整表创建的算法思路:
- 声明一结点p和计数器变量i;
- 初始化——空链表L;
- 让L的头结点的指针指向NULL,即建立一个带头结点的单链表;
- 循环:
- 生成一新结点赋值给 p;
- 随机生成一数字赋值给 p的数据域 p->data;
- 将p插入到头结点与前一新结点之间。
// 头插法 |
// 尾插法 |
单链表的整表删除
当我们不打算使用这个单链表时,我们需要把它销毁,其实也就是在内存中将它释放掉,以便于留出空间给其他程序或软件使用。
单链表整表删除的算法思路如下:
- 声明- -结点p和q;
- 将第一个结点赋值给p;
- 循环:
- 将下一结点赋值给 q;
- 释放p;
- 将q赋值给 p。
Status ClearList(LinkList *L) |
单链表结构与顺序表结构优缺点
存储分配方式
顺序存储结构用一段连续的存储单元依次存储线性表的数据元素
单链表采用链式存储结构,用组任意的存储单元存放线性表的元素
时间性能
查找
- 顺序存储结构O(1)
- 单链表O(n)
插入和删除
- 顺序存储结构需要平均移动表长一半的元素,时间为O(n)
- 单链表的插入和删除时间仅为O(1)
空间性能
顺序存储结构需要预分配存储空间,分大了,浪费,分小了易发生上溢
单链表不需要分配存储空间,只要有就可以分配,元素个数也不受限制
静态链表
用数组描述的链表叫做静态链表
循环链表
将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表( circular linked list)。
双向链表
双向链表(double linked list)是在单链表的每个结点中,再设置一个指向其前驱结点的指针域。所以在双向链表中的结点都有两个指针域。一个指向直接后继,另一个指向直接前驱。