目录
一、认识线性表
线性表是一个线性结构(数据元素之间存在一对一的关系)。它是由n(n>=0)个具有相同类型的数据元素a1,a2,a3.....组成的有限序列,这些数据元素称为结点,记录或表目。当n=0时,称为空表。
在较复杂的线性表中,一个数据元素可以由若干个数据项组成。
特点:
- 线性表中除第一个元素外,其他元素有且仅有一个直接前驱;
- 除最后一个元素外,其他元素有且仅有一个直接后继。
线性表的存储结构主要有以下两种:
- 定长的顺序存储结构,简称顺序表。程序通过创建数组,分配一块连续的存储空间来建立这种存储结构,主要特点为逻辑相邻,物理相邻。
- 变长的顺序存储结构,简称链表(链式存储结构)。链式存储结构利用指针将线性表中前后相邻的两个元素连接起来以表示数据元素的线性关系。主要特点为逻辑相邻,物理不一定相邻。
二、线性表的抽象数据类型
ADT List{
数据对象:D = { ai | ai∈ElemSet, i = 1, 2, …, n, n ≥ 0 }
数据关系:R = { <ai-1, ai> | ai-1, ai ∈D, i = 1, 2, …, n }
基本操作:
LinearList * Init_List( LinearList *L )
初始条件:线性表L不存在;
操作结果:构造一个空的线性表;
void Destory_List( LinearList *L)
初始条件:线性表L已经存在;
操作结果:销毁线性表L;
LinearList * Clear_List( LinearList *L )
初始条件:线性表L已经存在;
操作结果:将线性表L置为空表;
int List_Empty( LinearList *L)
初始条件:线性表L已经存在;
操作结果:若L为空表,则返回TURE(或1),否则返回FALSE(或0);
int List_Length( LinearList *L)
初始条件:线性表L已经存在;
操作结果:返回L中数据元素的个数;
int Getdata_List( LinearList *L,int i)
初始条件:线性表L已经存在,并且满足1<=i<=ListLength(L);
操作结果:将L中第i个元素值返回;
int Search_List(LinearList *L,int e,int compare())
初始条件:线性表L已经存在,compare()为数据元素判定函数;
操作结果:返回L中第一个与e满足compare()的数据元素的位序。若不存在,则返回值为0;
void PriorElem(LinearList *L,int e,int *pre_e)
初始条件:线性表L已经存在;
操作结果:若e是L的数据元素,而且不是第一个数据元素则用pre_e返回他的前驱;
否则操作失败,pre_e无意义。
void PriorElem(LinearList *L,int e,int *next_e)
初始条件:线性表L已经存在;
操作结果:若e是L的数据元素,而且不是最后一个数据元素则用next_e返回他的后继;
否则操作失败,next_e无意义。
LinearList *Insert_List( LinearList *L,int i,int e )
初始条件:线性表L已经存在并且满足1<=i<=ListLength(L)+1;
操作结果:在L中第i个位置前插入新的数据元素,L的长度+1;
LinearList *Delete_List( LinearList *L,int i,int *e )
初始条件:线性表L已经存在并且非空满足1<=i<=ListLength(L);
操作结果:删除L中第i个位置的数据元素,并用e返回其值,L的长度-1;
}ADT List;
三、线性表的顺序存储结构
1.顺序存储定义
线性表的顺序存储结构是指用一段地址连续的存储单元依次存储线性表的数据元素。
在程序设计中,用一维数组来实现顺序存储。
#define MAXSIZE 30 //存储空间初始分配量
typedef int ElemType;
typedef struct
{
ElemType data[MAXSIZE];/*数组存储数据元素,最大值为MAXSIZE*/
int length; /*线性表当前长度*/
}SqList;
建立顺序表的三个属性:
1)存储空间的起始位置(数组名data)
2)顺序表最大存储容量(MaxSize)
3)顺序表当前的长度(length)——有效数据的长度
2.数据长度和线性表长度的区别
- 数组的长度是存储线性表的存储空间的长度,存储分配后这个量一般是不变的。(特殊情况下数组还可以动态分配空间,存储数组的空间是在程序执行过程中通过动态存储分配语句分配 )
- 线性表长度是线性表中元素的个数,随着线性表的插入和删除操作的进行这个量是变化的。
- 在任意时刻数组的长度都是大于等于线性表的长度。
3.地址计算方法
数组元素的序号和存储下标的关系示意图
总结
-
1.顺序表最主要的特点是随机访问(C语言中基于数组),即通过首地址和元素序号可以在O(1)的时间内找到指定的元素。
-
2.顺序表的存储密度高,每个结点只存储数据元素。无需给表中元素花费空间建立它们之间的逻辑关系(因为物理位置相邻特性决定)
-
3.顺序表逻辑上相邻的元素物理上也相邻,所以插入和删除操作需要移动大量元素。
四、顺序表的插入和删除
1.插入操作
1)插入算法的思路
- 判断插入位置i是否合理,如果位置不合理抛出异常(如果线性表长度大于等于数组长度,则抛出异常或动态增加容量)
- 从最后一个元素开始向前遍历到第i个位置,分别将它们向后移动一个位置;
- 将要插入的元素填入i位置处
- 表长加1.
2)分析
-
最好情况:在表尾插入(即i=n+1),元素后移语句将不执行,时间复杂度为O(1)。
-
最坏情况:在表头插入(即i=1),元素后移语句将执行 n次,时间复杂度为O(n)。
-
平均情况:假设pi(pi=1/(n+1) )是在第i个位置上插入 一个结点的概率,则在长度为n的线性表中插入一个结点时所需移动结点的平均次数为(n-1)/2
2.删除操作
1)删除算法的思路
- 判断删除位置i是否合理,如果删除位置不合理,抛出异常;
- 取出删除元素
- 从删除位置开始遍历到最后一个位置,分别将它们都向前移动一个位置
- 表长减1
2)分析
-
最好情况:删除表尾元素(即i=n),无须移动元素,时间复杂度为O(1)。
-
最坏情况:删除表头元素(即i=1),需要移动除第一个元素外的所有元素,时间复杂度为O(n)。
-
平均情况:假设pi(pi=1/n)是删除第i个位置上结点的概率,则在长度为n的线性表中删除一个结点时所需移动结点的平均次数为(n-1)/2
五、顺序存储的优缺点
优点
- 无须为表示表中元素之间的逻辑关系而增加额外的存储空间
- 可以快速存取表中任意一位置的元素
缺点
- 插入和删除操作需要移动大量元素
- 当线性表长度变化较大时,难以确定存储空间的容量
- 造成存储空间的“碎片”