线性表的定义
线性表是具有相同数据类型的
n
(
n
>
=
0
)
n(n>= 0)
n(n>=0)个数据元素的有限序列,其中
n
n
n为表长,当
n
=
0
n=0
n=0时线性表是一个空表。若用
L
L
L命名线性表,则其一般表示为
L
=
(
a
1
,
a
2
,
.
.
.
,
a
i
,
a
i
+
1
,
.
.
.
,
a
n
)
L=(a_1,a_2,...,a_i,a_{i+1}, ..., a_n)
L=(a1,a2,...,ai,ai+1,...,an)式中,
a
1
a_1
a1是唯一的"第一个"数据元素,又称表尾元素。除第一个元素外,每个元素有且仅有一个直接前驱。除最后一个元素外,每个元素有且仅有一个直接后续。
所以,线性表的特点如下:
- 表中元素的个数有限
- 表中元素具有逻辑上的顺序性,表中元素有其先后次序
- 表中元素都是数据元素,每个元素都是单个元素
- 表中元素的数据类型都相同,这意味着每个元素占有相同大小的存储空间
- 表中元素元素具有抽象性,即仅讨论元素间的逻辑关系,而不是考虑元素究竟表示什么内容
注意:线性表是一种逻辑结构,表示元素之间一对一的相邻关系。顺序表和链表是指存储结构两者属于不同层面的概念,因此不要将其混淆。
顺序表的定义
线性表的顺序存储又称顺序表。它是用一组地址连续的存储单元依次存储线性表中的数据元素,从而使得逻辑上相邻的两个元素物理上也相邻。第1个元素存储在线性表的起始位置,第
i
i
i个元素的存储位置后面紧接着存储的是第
i
+
1
i+1
i+1个元素,称
i
i
i为元素
a
i
a_i
ai在线性表中的位序。因此,顺序表的特点是表中元素的逻辑顺序与其物理顺序相同
注意: 线性表中元素的位序是从1开始的,而数组中元素的下标是从0开始的。
顺序表的基本操作的实现
前期准备
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 50
typedef struct{
int data[MaxSize];
int length;
}SqList;
void InitList(SqList* L)
{
L->length = 0;
};
void Input(SqList* L)
{
int i;
printf("输入-1作为结束\n");
while (1 && L->length < MaxSize){
scanf("%d", &i);
if (i == -1)
break;
L->data[L->length++] = i;
}
};
void PrintList(SqList L)
{
if (L.length == 0)
printf("这是一个空列表\n");
else
{
printf("展示结果\n");
for(int i=0; i < L.length;i++)
printf("%d ", L.data[i]);
printf("\n");
}
};
(1) 插入操作
在顺序表 L L L的第 i ( 1 < = i < = L . l e n g t h + 1 ) i(1<=i<=L.length+1) i(1<=i<=L.length+1)个位置插入新元素 e e e。若 i i i的输入不合法,则返回0,表示插入失败;否则,将顺序表的第 i i i元素及其后的所有元素右移一个位置,腾出一个空位置插入新元素 e e e,顺序表长度加1,插入成功,返回1
/**************************************
顺序表的插入操作
L 表示原顺序表
i 表示插入的第i个位置
e 表示插入的值
return 如果可以插入返回1,否则返回0
**************************************/
int ListInster(SqList* L, int i, int e){
if(i < 1 || i > L->length + 1)
return 0;
if (L->length >= MaxSize)
return 0;
for(int j = L->length; j >= i; j--)
L->data[j] = L->data[j - 1];
L->data[i - 1] = e;
L->length ++;
return 1;
};
最好情况:在表尾插入(即
i
=
n
+
1
i=n+1
i=n+1),时间复杂度为
O
(
1
)
O(1)
O(1)
最坏情况:在表头插入(即
i
=
1
i=1
i=1),时间复杂度为
O
(
n
)
O(n)
O(n)
平均情况:假设
p
i
(
p
i
=
1
(
n
+
1
)
)
p_i(p_i=\frac{1}{(n+1)})
pi(pi=(n+1)1)是在第
i
i
i个位置上插入一个结点的概率,则在长度为
n
n
n的线性表中插入一个结点时,所需要移动结点的平均次数为
∑
i
=
1
n
+
1
p
i
(
n
−
i
+
1
)
=
∑
i
=
1
n
+
1
1
n
+
1
(
n
−
i
+
1
)
=
1
1
+
n
∑
i
=
1
n
+
1
(
n
−
i
+
1
)
=
1
1
+
n
n
(
n
+
1
)
2
=
n
2
\sum^{n+1}_{i=1}{p_i(n-i+1)}=\sum^{n+1}_{i=1}{\frac{1}{n+1}(n-i+1)}=\frac{1}{1+n}\sum^{n+1}_{i=1}(n-i+1)=\frac{1}{1+n}\frac{n(n+1)}{2}=\frac{n}{2}
i=1∑n+1pi(n−i+1)=i=1∑n+1n+11(n−i+1)=1+n1i=1∑n+1(n−i+1)=1+n12n(n+1)=2n
因此,线性表插入算法的平均时间复杂度为
O
(
n
)
O(n)
O(n)
(2)删除操作
删除顺序表 L L L中第 i ( 1 < = i < = L . l e n g t h ) i(1<=i<=L.length) i(1<=i<=L.length)个位置的元素,若成功则返回1,并将被删除的元素用引用变量 e m em em返回,否则返回0
/**********************************************
顺序表的删除操作
L 表示原顺序表
i 表示删除第i个位置
em 表示用指针返回被删除位置的值
return 如果可以删除返回1,否则返回0
**********************************************/
int ListDelete(SqList *L, int i, int *em)
{
if (i < 1 || i > L->length)
return 0;
*em = L->data[i-1];
for (int j = i; j<L->length; j++)
{
L->data[j - 1] = L->data[j];
}
L->length --;
return 1;
}
最好情况:在表尾删除(即
i
=
n
i=n
i=n),时间复杂度为
O
(
1
)
O(1)
O(1)
最坏情况:在表头删除(即
i
=
1
i=1
i=1),时间复杂度为
O
(
n
)
O(n)
O(n)
平均情况:假设
p
i
(
p
i
=
1
n
)
p_i(p_i=\frac{1}{n})
pi(pi=n1)是删除第
i
i
i个位置上结点的概率,则在长度为
n
n
n的线性表中删除一个结点时,所需要移动结点的平均次数为
∑
i
=
1
n
p
i
(
n
−
i
)
=
∑
i
=
1
n
1
n
(
n
−
i
)
=
1
n
∑
i
=
1
n
(
n
−
i
)
=
1
n
n
(
n
−
1
)
2
=
n
−
1
2
\sum^{n}_{i=1}{p_i(n-i)}=\sum^{n}_{i=1}{\frac{1}{n}(n-i)}=\frac{1}{n}\sum^{n}_{i=1}(n-i)=\frac{1}{n}\frac{n(n-1)}{2}=\frac{n-1}{2}
i=1∑npi(n−i)=i=1∑nn1(n−i)=n1i=1∑n(n−i)=n12n(n−1)=2n−1
因此,线性表删除算法的平均时间复杂度为
O
(
n
)
O(n)
O(n)
(3)按值查找(顺序查找)
在顺序表 L L L中查找第一个元素值等于 e e e的元素,并返回其位序。
/*************************************************
顺序表按值查找操作
L 表示原顺序表
e 表示在顺序表中找到的第一个元素值等于e的元素
return 如果可以找到返回其位序,否则返回0
**************************************************/
int LocateElem(SqList L, int e)
{
int i;
for (i=0; i < L.length; i ++)
if (L.data[i] == e)
return i+1;
return 0;
}
最好情况:查找的元素就在表头,时间复杂度为
O
(
1
)
O(1)
O(1)
最坏情况:查找的元素就在表尾,时间复杂度为
O
(
n
)
O(n)
O(n)
平均情况:假设
p
i
(
p
i
=
1
n
)
p_i(p_i=\frac{1}{n})
pi(pi=n1)是查找的元素在第
i
(
1
<
=
i
<
=
L
.
l
e
n
g
t
h
)
i(1<=i<=L.length)
i(1<=i<=L.length)个位置上的概率,则在长度为
n
n
n的线性表中查找值为
e
e
e时所需要比较的平均次数为
∑
i
=
1
n
p
i
∗
i
=
∑
i
=
1
n
1
n
∗
i
=
1
n
n
(
n
+
1
)
2
=
n
+
1
2
\sum^{n}_{i=1}{p_i*i}=\sum^{n}_{i=1}{\frac{1}{n}*i}=\frac{1}{n}\frac{n(n+1)}{2}=\frac{n+1}{2}
i=1∑npi∗i=i=1∑nn1∗i=n12n(n+1)=2n+1
因此,线性表按值查找算法的平均时间复杂度为
O
(
n
)
O(n)
O(n)
主函数代码
int main()
{
SqList* L;
int em;
L = (SqList *)malloc(sizeof(SqList));
int i=3, e=4, ii = 2;
int res;
InitList(L);
Input(L);
PrintList(*L);
# 插入
res = ListInster(L, i, e);
if (res==1)
printf("可以在第%d的位置上插入%d\n", i, e);
else
printf("无法插入\n");
PrintList(*L);
# 删除
res = ListDelete(L, ii, &em);
if (res==1)
printf("已经删除了第%d的位置上的%d\n", ii, em);
else
printf("无法删除\n");
PrintList(*L);
# 按值查找
res = LocateElem(*L, 4);
if (res==0)
printf("未找到该元素\n");
else
printf("4在列表的第%d个位置上\n", res);
return 0;
};