目录
一、顺序表的定义
线性表的顺序存储又称顺序表。是用一组地址连续的存储单元一次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素在物理位置上也相邻。第1个元素存储在线性表的起始位置,第i个元素的存储位置后面紧接着存储的是第i+1个元素,称i为元素ai在线性表中的位序。因此,顺序表的特点是表中元素的逻辑顺序与其物理顺序相同。
每个数据元素的存储位置都和线性表的起始位置相差一个和该数据元素的位序成正比的常数,因此,线性表中的任一数据元素都可以随机存取,所以线性表的顺序存储结构是一种随机存取的存储结构。通常用高级程序设计语言中的数组来描述线性表的顺序存储结构。
1、线性表的顺序存储类型——静态分配
#define MaxSize 50
typrdef struct{
ElemType data[MaxSize];//顺序表的元素
int length;//顺序表的当前长度
}SqList;//顺序表的类型定义
2、线性表的顺序存储类型——动态分配
#define InitSize 100//表长度的初始定义
typedef struct{
ElemType *data;//指示动态分配数组的指针
int MaxSize,length;//数组的最大容量和当前个数
}SeqList;//动态分配数组顺序表的类型定义
动态分配物理结构没有变化,是随机存取方式,只是分配的空间大小可以在运行时动态决定。
3、初始化分配语句
C语言的初始化分配语句为
L.data=(ElemType*)malloc(sizeof(ElemType)*InitSize);
C++的初始动态分配语句为
L.data=new ElemType[InitSize];
二、顺序表上基本操作的实现
1、插入操作
在顺序表L的第i(1<=i<=L.length+1)个位置后插入新元素e。若i的输入不合法,则返回false,表示插入失败;否则,将第i个元素及其后的所有元素依次往后移动一个位置,腾出一个空位置插入新元素e,顺序表长度增加1,插入成功,返回true。
bool ListInsert(SqList &L,int i,ElemType e){
if(i<1||i>L.length+1) return false;//判断i的范围是否有效
if(L.length>=MaxSize) return false;//当前存储空间已满,不能插入
for(int j=L.length;j>=i;j--) L.data[j]=L.data[j-1];//插入位置i及之后的元素后移
L.data[i-1]=e;//在i处插入e
L.length++;//插入成功,线性表长度加1
return true;
}
(1)较难理解的点
区别顺序表的位序和数组下标:思考一下,为何判断插入位置是否合法是,if语句中使用的是length+1,而移动元素的for语句中只用length?
在if语句中,我们要判断的是i的插入位置是否有效,而i可以插入的位置为从第1个元素之后到最后一个元素之后,及1<=i<=L.length+1。
(2)时间复杂度分析
最好情况:在表尾插入(即i=n+1),元素后移语句将不会执行,时间复杂度为O(1)
最坏情况:在表头插入(即i=1),元素后移语句将执行n次,时间复杂度为O(n)
平均情况:假设pi(pi=1/(n+1))是在第i个位置上插入一个结点的概率,则在长度为n的线性表中插入一个结点时,需要移动结点的平均次数为
∑
i
=
1
n
+
1
p
i
(
n
−
i
+
1
)
=
∑
i
=
1
n
+
1
(
n
−
i
+
1
)
/
(
n
+
1
)
=
(
1
/
(
n
+
1
)
)
∗
∑
i
=
1
n
+
1
(
n
−
i
+
1
)
=
(
1
/
(
n
+
1
)
)
∗
(
n
(
n
+
1
)
/
2
)
=
n
/
2
.
\sum_{i=1}^{n+1} p_i(n-i+1) =\sum_{i=1}^{n+1} (n-i+1)/(n+1)=(1/(n+1))*\sum_{i=1}^{n+1}(n-i+1)=(1/(n+1))*(n(n+1)/2)=n/2\,.
i=1∑n+1pi(n−i+1)=i=1∑n+1(n−i+1)/(n+1)=(1/(n+1))∗i=1∑n+1(n−i+1)=(1/(n+1))∗(n(n+1)/2)=n/2.
因此,线性表插入算法的平均时间复杂度为O(n)
2、删除操作
删除顺序表L中第i(1<=i<=L.length)个位置的元素,用引用变量e返回。若i的输入不合法,则返回false;否则,将被删元素赋给引用变量e,并将第i+1个元素及其后的所有元素依次往前移动一个位置,返回true。
bool ListDelete(SqList &L,int i,Elemtype &e){
if(i<1||i>L.length) return false;//判断i的范围是否有效
e=L.data[i-1];//将被删除的元素复制给e
for(int j=i;j<L.length;j++) L.data[j-1]=L.data[j];//将第i个位置后的元素前移
L.length--;//线性表长度减1
return true;
}
(1)时间复杂度分析
最好情况:删除表尾元素(即i=n),无须移动元素,时间复杂度为O(1)
最坏情况:删除表头元素(即i=1),需移动除表头元素外的所有元素,时间复杂度为O(n)
平均情况:假设pi(pi=1/n)是删除第i个位置上结点的概率,则在长度为n的线性表中删除一个结点时,所需移动结点的平均次数为
∑
i
=
1
n
p
i
(
n
−
i
)
=
∑
i
=
1
n
(
n
−
i
)
/
n
=
(
1
/
n
)
∗
∑
i
=
1
n
(
n
−
i
)
=
(
1
/
n
)
∗
(
n
(
n
−
1
)
/
2
)
=
(
n
−
1
)
/
2
.
\sum_{i=1}^{n} p_i(n-i) =\sum_{i=1}^{n} (n-i)/n=(1/n)*\sum_{i=1}^{n}(n-i)=(1/n)*(n(n-1)/2)=(n-1)/2\,.
i=1∑npi(n−i)=i=1∑n(n−i)/n=(1/n)∗i=1∑n(n−i)=(1/n)∗(n(n−1)/2)=(n−1)/2.
因此,线性表删除算法的平均时间复杂度为O(n)
可见,顺序表中插入和删除操作的时间主要耗费再移动元素上,而移动元素的个数取决于插入和删除元素的位置。
3、按值查找(顺序查找)
在顺序表L中查找第一个元素值等于e的元素,并返回其位序。
int LocateElem(SqList L,ElemType e){
int i;
for(int i=0;i<L.length;i++) if(L.data[i]==e) return i+1;//下标为i的元素值等于e,返回其位序i+1
return 0;//查找失败,退出循环
}
(1)时间复杂度分析
最好情况:查找的元素就在表头,仅需比较一次,时间复杂度为O(1)
最坏情况:查找的元素在表尾(或不存在)时,需要比较n次,时间复杂度为O(n)
平均情况:假设pi(pi=1/n)时查找的元素在第i(1<=i<=L.length)个位置上的概率,则在长度为n的线性表中查找值为e的元素所需比较的平均次数为
∑
i
=
1
n
p
i
∗
i
=
∑
i
=
1
n
(
i
/
n
)
=
(
1
/
n
)
∗
(
n
(
n
+
1
)
/
2
)
=
(
n
+
1
)
/
2
.
\sum_{i=1}^{n} p_i*i =\sum_{i=1}^{n}( i/n)=(1/n)*(n(n+1)/2)=(n+1)/2\,.
i=1∑npi∗i=i=1∑n(i/n)=(1/n)∗(n(n+1)/2)=(n+1)/2.
因此,线性表按值查找算法的平均时间复杂度为O(n)
本文为个人学习总结所得,如有错误欢迎指正,如有问题欢迎探讨。