一.顺序表的示意图
#define LIST INIT SIZE 100 //线性表存储空间的初始分配量;就是数组多大,这里定义了100
typedef struct{
Elem Type elem[LIST INIT SIZE];
//用一维数组来装数据元素,这里是Elem类型,也可以是别的类型
int length; //当前长度
}SqList;
这个就相当于定义了一个顺序表 L,它是一个SqList型的变量,其中我们可以根据定义来画出其示意图:
注意:1.数组是从a0开始的,所以开辟100个空间,就要到a99,绿色部分是开辟的数组空间;蓝色的部分是开辟的存储长度变量的部分。
2.对于普通变量来说其引用成员的写法如图,对于指针型来说应写成L->elem这样的
二.顺序表的基本函数状态代码
就是当结果是true、ok时,电脑运行返回的是1;当结果是false、error时,返回的结果是0,infeasible时是-1;overflow是-2;
三.顺序表中重要的操作
1.先行标的初始化、摧毁、清空求长度、判断是否为空(基本操作)
❶ 线性表L的初始化
Status InitList_Sq(SqList&L){ //构造一个空的顺序表
L.elem=new Elem Type[MAXSIZE]; //为顺序表分配空间
if(!L.elem) exit(OVERFLOW);
//如果不存在“线性表的成员”(存储空间分配失败),返回overflow,即-2
L.length=0; //空表长度为0
return OK; //
}
❷ 摧毁一个线性表(注意摧毁不会清空) ;清空线性表
//摧毁线性表
void DestroyList(SqList & L){
if(L.elem) delete L.elem; //如果是线性表单元就删除他,即释放空间
}
//清空线性表
void ClearList(SqList&L){
L.length=0; //这个是将线性表的长度置0
}
/*注意:摧毁和清空是不一样的,上面的摧毁是把开辟出来的所有空间都删除;清空是把开辟出来的空间中的顺序表的部分给置零*/
❸求线性表L的长度;判断线性表L是否为空
//求线性表L的长度
int IsEmpty(SqList L){
return(L.length);
}
//判断线性表L是否为空
int IsEmpty(SqList){
if(L.length==0) return 1;
else return 0;
}
2.顺序表的取值(根据位置i获得相应的位置数据元素的内容)
▲注意,我们在取值的时候也要先考虑i的位置是否合理,i只能在1~L中;
▲并且需要注意,逻辑位序和物理位序是相差1的,就是说第i个数据元素存储在第i-1个位置上;
int GetElem(SqList l,int i,ElemType &e){
if(i<i||i>L.length) return ERROR; //判断是否合理,不合理就返回ERROR
e=L.elem[i-1];
//注意第i个数据元素在第i-1个位置上!!!(因为数组是从a0开始的!而顺序表第一个数是i1!)
return Ok;
}
3.顺序表的查找
具体操作
·在线性表中找与指定值e相同的数据元素的位置
·从表的一端开始,逐个进行记录的关键字和给定值的比较。找到,返回该元素的位置序号;未找到,返回0。
int LocateElem(Sqlist L,ElemType e){
//在线性表中查找值为e的数据元素,返回填写序号(第几个元素)
for(i=0;i<L.length;i++) //这个是从a0开始查找一直到L,但是对于序号,是1,2···l+1这样的
if(L.elem[i]==e) return i+1; //位序比逻辑顺序多1!
return 0; //没找到,返回0
}
(L.elem[ ]中的都是数组的位置,之外的都是数据的位置,数组位置总是比数据的真实为止少1)
顺序表查找的算法分析
▧平均查找长度ASL(Average Search Length):
为了确定记录在表中的位置,需要与给定值进行比较的关键字的个数的期望值叫做查找算法的平均查找长度
具体的分析:
对于其时间效率来说,我们要找到他的基本操作,我们可知其基本操作为for循环中的if语句,即“L.elem[i]==e”
假设顺序表为下图:
假设我们要找的e在a的位置,那么我们只执行一次;e=b,2次; e=c,3次;·······e=g,7次,
那么我们就认为其时间复杂度为:(1+2+3+····+7)/7=4次 就相当于去一个平均频率。
所以以此类推,对于含有n个记录表,查找成功时
由此也可以拿上述例子验证,上述例子中,n=7,ASL=(7+1)/2=4,同上述结果,
所以由此可知,O(x)=n
三.顺序表的插入
线性表的插入运算是指在表的第i(1<=i<=n+1)个位置上,插入一个新结点e,使长度为n的线性表(a1,a2...ai-1,ai...an)变成长度为n+1的线性表(a1,a2,...ai-1,e,ai....an)
·插入的可能:插入在最后;插入在中间;插入在首端
·插入时的注意:1.i 的位置是否合法;
2.表的存储空间是否已经满了
▧ 算法思想:
1.2.见插入时注意事项;
3.将第n至第i位依次向后移动一个位置,空出第i个位置;(注意,是从最后一个开始依次移动!)
4.把新元素放入第i个位置;
5.表长加1,插入成功返回ok
具体算法:
Status Listlnsert_Sq(SqList &L,int i,ElemType e){
if(i<1||i>L.length+1) return ERROR; //判断i是否合理,i可以在最后,所以这里是L+1
if(L.length==MAXSIZE) return ERROR; //判断表的存储空间是否已经满了
for(j=L.length-1;j>=i-1;j--) //从最后开始判断,如果不是想要插入的位置
L.length[j+1]=L.elem[j]; //就用赋值的方式后移,然后在向前比较(j--实现)
L.elem[i-1]=e; //找到位置后,把其赋值为e
L.length++; //总长度加1,返回ok
return OK;
}
时间复杂度的分析:
这个基本操作也是for循环的赋值语句,算法同上,也是用期望值,但是注意,这里的每个查找概率是1/n+1个!因为你是从L+1的地方开始,而上面的查找是从第L个查找,所以这里的平均次数为: (pi是概率,都为1/(n+1);Ci是比较的次数)
因此其时间复杂度O(x)=n
五.表的删除(同插入)
顺序表的删除运算是指将表中的第i(1<=i<=L)个结点删除,使长度为L的线性表(a1...ai-1,ai,ai+1.....an),变成长度为n-1的线性表(a1...ai-1,ai+1....an)
算法思想:1.删除的位置i是否合法(合法值为1<=i<=n).
2.将第i+1~n位元素一次前移一位(这里是从i+1正向移动,与上面的插入相反)
3.表长减1,删除成功返回ok
具体算法:
Status ListDelete_Sq(SqList &L,int i){
if(i<1||i>L.length) return ERROR; //判断i是否合理
for(j=1;j<=L.length;j++) //从第一个开始向后找,找到后删除
L.length[j-1]=L.elem[j]; //然后后面的元素通过赋值的方法依次前移
L.length--; //总长度减1,返回ok
return OK;
}
时间效率分析:基本操作for循环中的赋值语句 L.length[j-1]=L.elem[j];
如果删除的是第一个,即i=1,那么后面的n-1个都要移动,移动n-1次;
i=2,··············n-2
i=3,··············n-3
···
i=n,···············0
所以我们可知:
那么他的时间复杂度为O(x)=n
上述可知,顺序表的操作算法分析:
·时间复杂度:查找、插入、删除平均时间复杂度为O(n);
·空间复杂度:显然我们在操作时,没有新开发出一片空间来辅助操作,所以S(n)=O(1)
综述顺序表的优缺点: