2.2线性表的实现
2.2.1线性表的顺序存储
顺序存储:把线性表的结点按逻辑顺序依次存放在一组地址连续的存储单元里。
用这种方法存储的线性表简称顺序表
顺序存储的线性表的特点:
- 线性表的逻辑顺序与物理顺序一致;
- 数据元素之间的关系是以元素在计算机内“物理位置相邻”来体现。
设每个元素需占用/个存储单元,以所占的第一个单元的存储地址作为数据元素的存储位置。则线性表中第i+1个数据元素的存储位置LOC(ai+1)和第i个数据元素的存储位置LOC(ai)之间满足下列关系:
LOC(ai+1) = LOC(ai)+ /
线性表的第i个数据元素ai的存储位置为:
LOC(ai) = LOC(a1)+(i-1)* /
存取结构:存取结构是在一个数据结构上对查找操作的时间性能的一种描述
-
随机存取结构:指在一个数据结构上进行查找的时间性能是O(1),即查找任意一个数据元素的时间是相等的,均为常数时间;
(顺序表是一种随机存储结构)
-
顺序存取结构:指在一个数据结构上进行查找的时间性能是O(n),即查找一个数据元素的时间复杂度是线性的,与该元素在结构中的位置有关。
(单链表是一种顺序存取结构)
数组具有随机存取的特性,顺序表还应该有表示线性表的长度属性,所以用结构类型来定义顺序表类型。
(1)静态结构:表一旦装满,不能扩充
#define MAX_SIZE 100
typedef int Status;
typedef int ElemType;
typedef struct sqlist{
ElemType Elem_array[MAX_SIZE];
int length;
}sqList;
(2)动态结构:可以扩充,新的大小计入数据成员maxSize中
typedef struct sqlist{
ElemType *elem;//存储数组
int length;//当前表元素个数
int maxSize;//表的最大长度
}sqList;
顺序线性表的插入
在线性表L = (a1,…ai-1,e,ai,ai+1,…,an)
实现步骤:
(1)将线性表L中的第i个至第n个结点后移一个位置;
(2)将结点e插入到结点ai-1之后;
(3)线性表长度加1。
如下图所示:
Status Insert_SqList(Sqlist *L,int i,ElemType e){
int j;
if (i<0||i>L->length-1) return ERROR;
if (L->length>=MAX_SIZE){
printf("溢出!\n");return ERROR;
}
for (j=L->length-1;j>=i-1;--j)
L->Elem_array[j+1] = L->Elem_array[j];
/* i-1位置以后的所有结点后移 */
L->Elem_array[i-1] = e;/* 在i-1个位置插入 */
L->length++;
return OK;
}
时间复杂度分析
线性表L中的第i个元素之前插入新结点,结点的移动次数来估计算法的时间复杂度。
设在线性表L中的第i个元素之前插入结点的概率为Pi,不失一般性,设各个位置插入是等概率,则Pi = 1/(n+1),而插入时移动结点的次数为n-i+1。
总的平均移动次数:E(Insert) = ε pi *(n-i+1)(1<=i<=n)
所以:E(Insert) = n / 2 。
即在顺序表上做插入运算,平均要移动表上的一半结点。当表长n较大时,算法的效率相当低。
因此算法的平均时间复杂度为O(n)。
顺序线性表的删除
在线性表L = (a1,…,ai-1,ai,ai+1,…,an)中删除结点ai(1<=i<=n),使其称为线性表:
L = (a1,…,ai-1,ai+1,…,an)
实现步骤:
(1)将线性表L中的第i + 1个至第n个结点依次向前移动一个位置。
(2)线性表长度减1。
如下图所示:
ElemType Delete_sqList(Sqlist *L,int i){
int k;ElemType x;
if (L->length==0){
printf("线性表L为空!\n");
return ERROR;
}
else if(i<1||i>L->length){
printf("要删除的数据元素不存在!\n");
return ERROR;
}
else {
x=L->Elem_array[i-1];
/*保存结点的值*/
for (k=i;k<L->length;k++)
L->Elem_array[k-1]=L->Elem_array[k];
/*i位置以后的所有结点前移*/
L->length--;
return (x)
}
}
时间复杂度分析
设在线性表L中删除第i个元素的概率为Pi,不失一般性,设删除各个位置是等概率,则Pi=1/n,而删除时移动结点的次数为n-i。
总的平均移动次数:E(delete)= ε pi * (n-i) (1<=i<=n)
所以:E(delete)= (n-1)/ 2。
即在顺序表上做删除运算,平均要移动表上一半 结点。当表长n较大时,算法的效率相当低。
因此算法的平均时间复杂度为O(n)。
顺序线性表的查找定位删除
在线性表 L = (a1,a2,… ,an)中删除值为x的第一个结点。
实现步骤:
(1)在线性表L查找值为x的第一个数据元素。
(2)将从找到的位置至最后一个结点依次向前移动一个位置。
(3)线性表长度减1。
Status Locate_Delete_sqList(Sqlist *L,ElemType x)
/*删除线性表中值为x的第一个结点*/
{
int i=0,k;
while (i<L->length) /*查找值为x的第一个结点*/
{
if (L-> Elem_array[i]!=x)i++;
else{
for (k=i+1;k<L->length;k++)
L->Elem_array[k-1]=L->Elem_array[k];
L->length--;break; }
if (i>L->length){
printf("要删除的数据元素不存在!\n");
return ERROR;}
return OK;
}
}
时间复杂度分析-数据元素的比较和移动操作
首先,在线性表L中查找值为x的结点是否存在;
其次,若值为x的结点存在,且在线性表L中的位置为i,则在线性表L中删除第i个元素。
设在线性表L删除数据元素概率为Pi,不失一般性,设各个位置是等概率,则Pi = 1/n。
比较的平均次数:E(compare) = ε Pi * i (1<=i<=n)
所以:E(compare) = (n + 1)/ 2。
删除时平均移动次数:E(delete)= ε Pi * (n - 1) (1<=i<=n)
所以:E(delete)= (n-1) / 2。
平均时间复杂度:E(compare)+ E(delete) = n,即为O(n)。
例1:线性表的合并问题
已知顺序表LA和LB中的数据元素按值非递减有序排列,现要将LA和LB归并为一个新表LC,且LC中的数据元素仍按值非递减有序排列。
LA = (3,5,8,9) LB = (2,6,9,11,15,20)
LC = (2,3,5,6,8,9,9,11,15,20)
/*部分核心代码*/
void MergeList(SqList La,SqList Lb,SqList *Lc){
1. pa = La.elem;pb = La.elem;
2. Lc->listsize = Lc.length = La.length+Lb.length;
3. Pc = Lc->elem = (ElemType*)malloc(Lc.listsize*sizeof(ElemType));
4. if(!Lc.elem) exit(overfiow);
5. pa_last = La.elem + La.length-1;
6. pb_last = Lb.elem + Lb.length-1;
7. while(pa<=pa_last&&pb<=pb_last){
8. if (*pa<=*pb)
9. *pc++=*pa++;
10. else
11. *pc++=*pb++;}
12. while(pa<=pa_last) *pc++=*pa++;
13. while(pb<=pb_last) *pc++=*pb++; }