顺序存储结构的插入与删除
0.顺序表的初始化
#define MAXSIZE 100 //顺序表可能达到的最大长度
#define OK 1
#define ERROR 0
typedef int Status //Status是int的类型别名
typedef int ElemType; //ElemType是int的类型别名
//顺序表的存储结构
typedef struct{
ElemType data[MAXSIZE]; //存储顺序表中数据元素的数组
int length; //顺序表的当前长度
}SqList; //顺序表的结构类型为SqList(指针)
SqList L; //定义L
//初始化顺序表(存储/物理结构上操作)
Status InitList(SqList &L){ //引用&避免拷贝,以下函数内对L的操作就是对主调函数内参数的操作
L.length = 0; //空表长度为0
return OK; //返回初始化状态
}
1.获得数据元素的操作
线性表中数据元素从1开始
数组中存储的数据元素下标从0开始
所以,如果想要获得线性表中第
i
i
i 个位置的数据元素,则应该获得数组中第
(
i
−
1
)
(i-1)
(i−1) 个位置的值
c语言描述
/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */
/* 操作结果:用e返回L中第i个数据元素的值,注意i是指位置,第1个位置的数组是从0开始 */
Status GetElem(SqList L,int i,ElemType *e) //返回类型为Status(本质为int)是函数处理的状态
{
//C语言有取地址,没有引用
//为什么这里e用指针??
//因为被调函数Status GetElem(...)在运行结束后会销毁内部变量(包括e)
//所以被调函数中的data[i-1]通过指针将其地址传递给主调函数GetElem(L,i,&e)中的e,才能将取到data[i-1]保留下来
//调用该函数:GetElem(L,i,&e) 这里&为取地址符
//如果是C++这里可以使用引用&,
//Status GetElem(SqList L,int i,ElemType &e){ e=L.data[i-1];}
//引用的话,相当于给访问到的元素起了个别名,函数结束后引用会被销毁
//所以通过给data[i-1]起别名,将其别名通过被调函数传递给主调函数,才能将取到data[i-1]保留下来
if(L.length==0 || i<1 || i>L.length) //线性表不能为空表 或者 下标不能为负数 或者 下标i不能超过线性表的长度
return ERROR; //否则返回0
*e=L.data[i-1]; //线性表的数据元素存储在数组,所以用线性表调用数组
//线性表和数组的起始下标差1
return OK;
}
c++描述(使用引用&)
//此处的i是数据元素在线性表中的位序,而非数组中的下标
// 下标 = 位序i - 1
Status GetElem(SqList L,int i,ElemType &e){ //此被调函数对进行了操作,通过引用将改变后的形参的值回传给了实参
if(i < 1 || i > L.length) //判断i是否处于合理位置
return ERROR;
e=L.data[i-1]; //将表对应的数组中的数据元素赋值给e
}
2.插入数据元素的操作
图解插入操作过程:
举例:
现有一张长度为length的线性表 L,表内有9个数字,已经将其9个数字存储到大小为 MAXSIZE 的数组当中
逻辑上:现在需要将线性表 L 中第
i
i
i 位置插入一个数字
操作上:在数组中将此数字插入至 下标
k
k
k 处
(
其
中
k
=
i
−
1
)
(其中k=i-1)
(其中k=i−1)插入数字前,将插入位置之后的所有数据元素向后移动一位,最后线性表长度加1
线性表是逻辑结构、数组是存储结构或物理结构
存储结构是数据的逻辑结构在计算机中的存储形式
线性表和数组的具体关系参考本人数据结构第一章博客:数据结构第一章图解
补充知识:
点运算符(.)和箭头运算符(->)均可调用结构体中的变量,但前者无法调用结构体中的指针变量,而后者可以
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 20 /* 存储空间初始分配量 */
typedef int ElemType; /* ElemType类型根据实际情况而定,这里假设为int */
typedef struct
{
ElemType data[MAXSIZE]; /* 数组,存储数据元素 */
int length; /* 线性表当前长度 */
}SqList; //SqList是结构体struct的别名
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
//插入操作函数
/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L), */
/* 操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1 */
//注意:线性表从1开始,数组下标从0开始
//线性表位序用 i 表示,数组的下标用 k 表示
// k = i - 1
Status ListInsert(SqList *L,int i,ElemType e) //因为要对线性表进行改动,所以参数使用指针形式
{
int k; //数组下标定义
//根据线性表判断是否要进行操作
if (L->length==MAXSIZE) /* 顺序线性表已经满,无法再向数组插入数据元素 */
//length为线性表长度,MAXSIZE为数组长度
//L->length 提取线性表L的成员变量length与数组最大存储MAXSIZE比较
return ERROR;
if (i<1 || i>L->length+1)/* 当i比第一位置小或者比最后一位置后一位置还要大时 */
//L->length+1 提取结构体L的成员变量length并将其加1
return ERROR;
//根据线性表判断是否要进行操作
if (i<=L->length) /* 若插入数据位置不在表尾 */
{
// i 为线性表中的位序
// L->length 提取线性表L的成员变量length
// 插入位置在线性表内部
//满足以上判断条件,可对数组进行操作
for(k=L->length-1;k>=i-1;k--) /* 将要插入位置之后的数据元素向后移动一位 */
//k=L->length-1 数组最后一个元素的位置,从此遍历到要插入数组中的位置i-1
L->data[k+1]=L->data[k]; //将数组中满足要求范围内的元素后移一位
//L->data[..]调用线性表对应的数组
}
//数组中的操作
L->data[i-1]=e; //在要插入的位置处插入元素e
//线性表中的操作
L->length++; //线性表的长度加1
return OK;
}
我们假设将
i
=
4
i=4
i=4 位置插入数字 10
3.删除数据元素的操作
逻辑上:删除线性表L中第
i
i
i 个位置上的数据元素
操作上:删除数组data中第
i
−
1
i-1
i−1 上的数据元素,删除指定位置的元素后,将此位置之后的所有数据元素向前移动一位,最后线性表长度减1
/* 初始条件:顺序线性表L已存在,1≤i≤ListLength(L) */
/* 操作结果:删除L的第i个数据元素,并用e返回其值,L的长度减1 */
Status ListDelete(SqList *L,int i,ElemType *e) //因需要多线性表L进行改动,所以参数使用指针
{
int k; //数组下标定义
//根据线性表判断是否要进行操作
if (L->length==0) /* 线性表为空 */
return ERROR;
if (i<1 || i>L->length) /* 删除位置不正确 */
return ERROR;
//移除指定位置的元素
//通过指针将data[i-1]值传递给主调函数实参e,虽然被调函数结束后会销毁作用域内的变量,但指针保存的该值的地址并没有被销毁。如果使用普通变量,则会被销毁
*e=L->data[i-1]; //将数组中下标为 i-1 的元素赋值给指针e(相当于删除)
//根据线性表判断是否要进行操作
if (i<L->length) /* 如果删除不是最后位置 */
{
//数组操作
for(k=i;k<L->length;k++)/* 将删除位置后继元素前移 */
L->data[k-1]=L->data[k]; //将范围内的所有数据元素向前移动一位
}
L->length--; //线性表的长度减1
return OK;
}
举例:
假设我们删除
i
=
4
i=4
i=4 位置的数字10
结论:
线性表的顺序存储结构,
在读数据时,不管是哪个位置,时间复杂度都是
O
(
1
)
O(1)
O(1)
插入或删除时,需要移动大量元素,时间复杂度都是
O
(
n
)
O(n)
O(n)
由于数组有长度相对固定的静态特性,当表中数据元素个数较多且变化较大时,操作过程相对复杂,必然导致存储空间的浪费