复习数据结构线性表的基本操作
线性表:由n(n≥0)个数据特性相同的元素构成的有限序列称为线性表。元素个数n定义为线性表的长度,n=0时为空表。
线性表的基本特点: 除第一个元素无直接前驱,最后一个元素无直接后继外,其他每个数据元素都有一个前驱和后继。
线性表的顺序表示和实现
顺序存储表示:用一组连续的存储单元依次存储线性表的数据元素的方式
顺序存储特点:逻辑上相邻的数据元素,其物理次序也是相邻的
假设线性表的每个元素需占用n个存储单元,并以所在的第一个单元的存储地址作为数据元素的存储起始位置
则LOC(ai+1)=LOC(ai)+n,LOC(ai)=LOC(a1)+(i-1)*n.
线性表的顺序存储结构是一种随机存取的存储结构。
顺序表的基本操作
顺序表的抽象数据类型(ADT)
ADT list
{
数据类型:D={ai|ai∈ElemType,i=1,2,3,4,...}
数据关系:R={<a(i-1),ai>|a(i-1),ai∈D,i=1,2,3,4,...}
数据操作:InitList(&L)//创建一个空表
ListInsert(&L,i,e)//将值e插入到第i个位置中
ListDelete(&L,i)//删除第i个位置的数
GetElem(i,L)//取表中第i个位置的数
LocateElem(L,e)//查找表中值为e的序号
.
.
.
}
① 顺序表结构体
# define MAXSIZE 20 //顺序表的最大长度
typedef struct
{
ElemType *elem;//指向顺序表首地址的指针
int length;//表长
}Sqlist;
②创建一个空表
算法步骤:
①申请一组空间用来存放数组表中的值
②申请成功返回OK,失败返回FAIL.
Status InitList(Sqlist &L)
{
L.elem = (ElemType*)calloc(MAXSIZE,sizeof(ElemType));//使用calloc()函数申请MAXSIZE个连续的大小为ElemType的空间
L.length = 0;//初始化表长为0
if(!L.elem) return OK;
else return FAIL;
}
③顺序表的取值操作
算法步骤:
①判断i的合法性,当i≤0或i>L.length时,i值不合法,返回0
②返回L.elem[i-1](数组下标从零开始)
ElemType GetElem(int i,Sqlist L)
{
if(i <= 0 || i>L.length) return ERROR;//判断i的合法性
else return L.elem[i-1];//返回顺序表中第i个的值
}
算法分析
取值操作的时间复杂度为O(1)。
④顺序表的查找操作
算法步骤
① 将需要查找的值传入后,依次与表中的各数值比较,若查找成功,返回i+1(数组下标是从零开始的)。
②若查找失败,返回FAIL。
int LocateElem(Sqlist L,ElemType ListNumber)
{
int i;
for(i=0;i<L.length;i++)
if(L.elem[i] == ListNumber) return i+1;//依次与表中的各个值比较
return ERROR;
}
算法分析
在查找算法中,时间主要耗费在数据的比较上,而比较的次数取决于被查元素在表中的位置。查找时,为确定元素在顺序表中的位置,需和给定值进行比较的数据元素个数的期望值称为查找算法成功时的平均查找长度(ASL)。
ASL=Σ(i=1~i=n)(PiCi).
Ci取决于所查元素在表中的位置。
Pi为每个元素被查到的概率。
分析得:
Pi=1/n.
ASL=Σ(i=1~i=n)((1/n)i)=(n+1)/2.
所以顺序表的查找操作时间复杂度为O(n)。
⑤顺序表的插入操作
算法步骤
①判断插入位置i是否合法,若i≤0或i大于L.length+1或L.length=MAXSIZE。则返回ERROR。
②将最后一个元素到第i个的元素依次向后移动一位。空出第i个位置。
③将所要插入的值放入第i个位置。
④表长加1.
Status ListInsert(Sqlist &L,int i,ElemType NewNum)
{
int k;
if(i<=0||i>L.length+1||L.length==MAXSIZE) return FAIL;//判断i值的合法性
for(k=L.length-1;k>=i-1;k--)
L.elem[k+1]=L.elem[k];//将元素值依次向后移动
L.elem[i-1]=NewNum;//插入值放入第i个位置
L.length++; //表长加1
return OK;
}
算法分析
在顺序表插入的操作中,时间主要耗费在移动元素上.
如果插入到第i个位置上,则需要移动n-(i-1)个元素,即n-i+1个。
Eins(Σ(i=1~i=n+1)(PiCi)
Pi为每个位置被插入元素的概率
Ci为每个位置被插入后对应需要移动元素的个数。
分析得:
Pi=(1/(n+1)).
Ci=(n-i+1).
Eins(Σ(i=1~i=n+1)PiCi)=n/2.
所以,顺序表的插入操作的时间复杂度为O(n)。
⑥顺序表的删除操作
算法步骤
① 判断i值的合法性,当i<=0或i>L.length或L.length=0时,返回ERROR。
②将第i+1个元素至最后一个元素依次向前移动一位。
③表长减1.
Status ListDelete(Sqlist &L,int i)
{
int k;
if(i<0||i>L.length||L.length == 0) return FAIL;//判断i值的合法性
for(k = i-1; k <= L.length-1; k++)
L.elem[k] = L.elem[k+1];//将元素依次向前移动一位
L.length--;//表长减1
return OK;
//如果for循环中k的处置为i,则循环体中的语句变为L.elem[k-1] = L.elem[k].
}
算法分析
顺序表的删除操作与顺序表的插入操作类似,时间主要耗费在数据元素的移动上。
如果删除了第i个元素,则需要移动n-i个元素。
Eins(Σ(i=1~i=n)(PiCi)
Pi为每个位置元素被删除的概率
Ci为每个位置被删除后对应需要移动元素的个数。
分析得:
Pi=(1/n).
Ci=(n-i).
Eins(Σ(i=1~i=n)PiCi)=(n-1)/2.
所以,顺序表的删除操作的时间复杂度为O(n)。
“可见,线性表的顺序存储结构可以随机存取表中的任意元素,存储位置也可以用很简明的公式表示,但线性表的存储在插入和删除操作时,则需要移动大量的元素,操作过程较为复杂。”