之前学的时候没太在意,现在期末复习的时候还是做一下笔记吧,不然内心有点不安φ(..)
线性表的概念
定义:线性表是由n(n≥0)个类型相同的数据元素a1,a2,…,an组成的有限序列,记作(a1,a2,…,an)。
数据元素之间是一对一的关系,即每个数据元素最多有一个直接前驱和一个直接后驱。
特点:
-
同一性:线性表由同类数据元素组成,每一个ai必须属于同一数据对象。
-
有穷性:线性表由有限个数据元素组成,表长度就是表中数据元素的个数。
-
有序性:线性表中相邻元素之间存在着序偶关系<ai,ai+1>
线性表的顺序存储
线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素,使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中。
采用顺序存储结构的线性表通常称为顺序表。
假设线性表中每个元素占k个单元,第一个元素的地址为loc(a1),则第i个元素的地址为:
loc(ai)=loc(a1)+(i-1)*k
#define maxsize 线性表可能达到的最大长度
typedef struct{
ElemType elem[maxsize];
int length;//表长
}SeqList;
SeqList L;//或SeqList *L;
基本运算
- 查找
-
按序号查找GetData(L,i):
要求查找线性表L中第i个数据元素,其结果是L.elem[i]。 -
按内容查找Locate(L,x):
要求查找线性表L中与给定值x相等的数据元素,其结果是:若在表L中找到与x相等的元素,则返回该元素在表中的序号;若找不到,则返回一个“空序号”,如-1。
int Locate(SeqList L,ElemType x){
int i=1;
while(i<=L.length && L.elem[i]!=x)
i++;
if(i<L.length) return i;
else return -1;
}
- 插入
是指在表的第i(1≤i≤n+1)个位置,插入一个新元素e,使长度为n的线性表(e1,…,e1,e,…,en)变成长度为n+1的线性表(e1,…,e1,x,e,…,en)。
线性表(4,9,15,28,30,30,42,51,62),需在第4个元素之前插入一个元素“21”。则需要将第9个位置到第4个位置的元素依次后移一个位置,然后将“21”
插入到第4个位置。
(4,9,15,2128,30,30,42,51,62)
插入算法:
#define OK 1
#define ERROR 0
int InsList(SeqList *L,int i,ElemType x){
int k;
if(i<1 || i>L->length+1){
printf("插入位置i值不合法");
return ERROR;
}
if(L->length>=maxsize-1){
printf("表已满无法插入");
return ERROR;
}
for(k=L->length;k>=i;k--)
L->elem[k+1]=L->elem[k];
L->elem[i]=x;
L->length++;
return OK;
}
- 删除
是指将表的第i(1≤i≤n)个元素删去,使长度为n的线性表(e1,…,ei-1,ei,ei+1,…,en),变成长度为n-1的线性表(e1,,ei-1,ei+1,…,en)。
删除算法:
#define OK 1
#define ERROR 0
int DelList(SeqList *L,int i,ElemType x){
int k;
if(i<1 || i>L->length){
printf("删除位置不合法");
return ERROR;
}
*e=L->elem[i];
for(k=i;k<=L->length-1;k--)
L->elem[k]=L->elem[k+1];
L->length--;
return OK;
}
- 插入、删除算法时间复杂度分析
考虑移动元素的平均情况
- 插入算法的时间复杂度:
假设在第i个元素之前插入的概率为Pi,则在长度为n的线性表中插入一个元素所需要移动元素次数的期望值为:
Eis = ∑i=1n+1 pi(n-i+1) ,复杂度为O(n)。
若假定在线性表中任何一个位置上进行插入的概率都是相等的,则移动元素的期望值为:
Eis = 1/(n+1) * ∑i=1n+1 pi(n-i+1) = n/2 - 删除算法的时间复杂度
假设在第i个元素之前删除的概率为qi,则在长度为n的线性表中删除一个元素所需要移动元素次数的期望值为:
Edl = ∑i=1n qi(n-i) ,复杂度为O(n)。
若假定在线性表中任何一个位置上进行删除的概率都是相等的,则移动元素的期望值为:
Edl = 1/n * ∑i=1n qi(n-i) = (n-1)/2
练习:
有两个顺序表LA和LB,其元素均为非递减有序排列,编写一个算法,将它们合并成一个顺序表LC,要求LC也是非递减有序排列。
算法思想:设表LC是一个空表,为使LC也是非递减有序排列,可设两个指针i、j分别指向表LA和LB中的元LA.elem[i]>LB.elem[j],则当前先将LB.elem[j]插入到表LC中,若LA.elem[i]sLB.elem[j],当前先将LA.elemi]插入到表LC中,如此进行下去,直到其中一个表被扫描完毕,然后再将未扫描完的表中剩余的所有元素放到表LC中。
void merge(SeqList *LA,SeqList *LB,SeqList *LC){
i=1;j=1;k=1;
while(i<=LA->length && j<=LB->length){
if(LA->elem[i]<=LB->elem[j])
LC->elem[k++]=LA->elem[i++];
else
LC->elem[k++]=LB->elem[j++];
}
while(i<=LA->length)
LC->elem[k++]=LA->elem[i++];
while(j<=LB->length)
LC->elem[k++]=LB->elem[j++];
LC->length=LA->length+LB->length;
}
优点
①用数组存储数据元素,操作方法简单,容易实现。
②无须为表示结点间的逻辑关系而增加额外的存储开销。
③存储密度高。
④顺序表可按元素位序随机存取结点。
缺点:
①插入或删除运算不方便,除表尾的位置外,在表的其它位置上进行插入或删除操作都必须移动大量的结点,其效率较低;
②由于顺序表要求占用连续的存储空间,存储分配只能预先进行静态分配。因此当表长变化较大时,难以确定合适的存储规模。