线性表的顺序表示与实现:
-
顺序表示
顺序存储:将逻辑上相邻的数据元素存储到物理上也相邻的存储单元,即逻辑上相邻,物理上也相邻。
基地址:顺序表中第一个元素 a 1 a_{1} a1 的存储位置,由于线性表的顺序存储结构占用的是一片连续存储空间,因此知道首元素的地址就可以通过元素下标访问任意元素。
随机访问:又称随机存取,指访问线性表中任意元素的时间相同,线性表就是顺序存储。
-
特点与实现
顺序表中的数据元素具有以下特点:
a. 地址连续;
b. 依次存放;
c. 随机存取;
d. 类型相同;
这些特点与数组元素相似性很高,考虑采用一维数组的形式来实现顺序表:
//使用 *静态分配* 数组定义线性表
# define Max_size 1000 //顺序表存储空间的初始分配大小
typedef char ElemType; //定义数据元素类型
typedef struct{
ElemType elem[Max_size];
int length; //顺序表表长(即数据元素个数)
} Sqlist;
*由于线性表长度可变(插入、删除等操作)但数组长度不可动态定义,因此通过整型变量 length 来表示顺序表的
长度属性(即定义一个大数组,使用其中的一部分作为线性表)
# define Max_size 1000 //顺序表存储空间的初始分配大小
typedef char ElemType; //定义数据元素类型
typedef struct{
ElemType * elem;
int length; //顺序表表长(即数据元素个数)
} Sqlist;
Sqlist L;
L.elem = (ElemType *)malloc(sizeof(ElemType) * Max_size); //动态分配内存空间
L.length = 0; //空表(length = 0)
*通常建议采用动态分配内存的数组作为建立顺序表的对象
*一般而言,顺序表的定义分为 3 步:
1)定义存放线性表所用的一维数组的最大长度(最大表长);
2)定义顺序表的数据元素类型;
3)声明线性表,分配内存空间并初始化表长。
由此得到的顺序表如下:
通过
L
.
e
l
e
m
[
i
]
L.elem[i]
L.elem[i] 访问第
i
+
1
i+1
i+1 个成员;通过
L
.
l
e
n
g
t
h
L.length
L.length 访问顺序表成员数量(即表长)
需要注意的是,顺序表的逻辑位序与物理位序相差1。
Eg. 图书表的顺序存储结构类型定义:
//图书表定义
# define Max_size 10000 //图书表最大长度
//声明图书元素
typedef struct{
char no[20]; //ISBN
char name[50]; //图书名字
float price; //图书价格
} Book;
typedef struct{
Book * elem;
int length; //图书表表长
} Book_Sqlist;
- 基本操作与其实现
3.1 初始化线性表 L L L:
//异常
# define OVERFLOW -2 //溢出异常
# define OK 0 //正常
typedef int status;
//初始化顺序表L
status InitSqList(SqList &L){
L.elem = new ElemType[Max_size]; //动态分配内存空间
if(!L.elem){ //检查是否分配内存成功
exit(OVERFLOW); //分配失败则退出程序
}
L.length = 0; //初始化表长
return OK;
}
*return 返回一个值给其上层函数,但是不立刻结束程序,其中0表示正常结束,-1表示程序异常。
*exit(int) 立刻终止程序,其中exit(0)表示正常退出,除0以外的值都表示异常退出。
3.2 销毁顺序表 L L L:
//销毁顺序表L
void DestroyList(SqList &L){
if(L.elem) //检测线性表L是否存在
delete L.elem; //释放线性表L的存储空间
}
3.3 清空顺序表 L L L:
//清空线性表L
void ClearList(SqList &L){
L.length = 0; //将表长L.length重置为0
}
*虽然此时线性表L中的elem依然有元素,但是在下次使用时会覆盖
3.4 获得顺序表 L L L长度:
//获得线性表L的表长
int GetLength(SqList L){
return L.length; //返回线性表表长
}
3.5 判断顺序表 L L L是否为空:
//判断线性表L是否为空
int IsEmpty(SqList L){
if(L.length == 0) //如果为空,返回1
return 1;
else
return 0; //否则,返回0
}
3.6 顺序表 L L L取值:
# define ERROR 1 //错误
//顺序表的取值
//依据位置i获取相应位置数据元素的内容
int GetElem(SqList L, int i, ElemType &e){
if(i<1||i>L.length)
return ERROR; //判断取值位置是否合法
e = L.elem[i-1]; //将对应成员的值返回变量e
return OK;
}
3.7 顺序表
L
L
L的查找:
算法思想:
在顺序表中从序号1一直查找到最后一个元素,找到则退出循环,返回元素序号;否则查找失败。
//顺序表的查找
//按值查找:在顺序表中查找与指定值e相同的数据元素,返回其序号
int LocateElem(SqList L, ElemType e){
for (int i = 0; i < L.length; i++)
if(L.elem[i]==e)
return i + 1; //查找成功则返回其序号
return 0;
}
*算法分析
为确定记录在表中的位置,需要与给定值进行比较的关键字的个数的期望即为平均查找长度ASL。因此对含有n个元素的顺序表,查找成功时有
A
S
L
=
∑
i
=
1
n
P
i
C
i
ASL = \sum_{i=1}^{n} P_{i}C_{i}
ASL=∑i=1nPiCi
其中
P
i
P_{i}
Pi为第
i
i
i个元素被查找的概率;
C
i
C_{i}
Ci为找到第
i
i
i个元素需要比较的次数;
∵
\because
∵ 顺序表中各个元素被查找的概率相等,
P
i
=
1
n
P_{i}=\frac{1}{n}
Pi=n1,
C
i
=
i
C_{i}=i
Ci=i;
∴
\therefore
∴
A
S
L
=
∑
i
=
1
n
i
n
=
1
2
(
n
+
1
)
ASL = \sum_{i=1}^{n}\frac{i}{n}=\frac{1}{2}(n+1)
ASL=∑i=1nni=21(n+1)
∴
\therefore
∴
T
(
n
)
=
O
(
n
)
T(n) = O(n)
T(n)=O(n)
3.8 顺序表
L
L
L的插入:
算法思想:
Step.1 判断插入位置
i
i
i是否合法 ,线性表
L
L
L是否会溢出;
Step.2 将第
n
n
n位至第
i
i
i位的元素依次后移一个位置,空出位置
i
i
i;
Step.3 将新元素
e
e
e插入到位置
i
i
i;
Step.4 表长L.length+1,返回OK。
//顺序表的插入
status SqListInsert(SqList &L, int i, ElemType e){
if(i<1||i>L.length+1)
return ERROR; //插入位置i不在[1,n+1]中,返回ERROR
if(L.length == Max_size)
return ERROR; //顺序表已满,继续插入会造成溢出
for (int j = L.length - 1; j >= i - 1; j--){
L.elem[j + 1] = L.elem[j]; //将[i,n]的元素依次后移
}
L.elem[i - 1] = e; //插入元素e
L.length += 1; //表长+1
return OK;
}
插入位置:
1) 插入位置在开头(i = 1);
2) 插入位置在中间(i = 2~n);
3) 插入位置在最后(i = n+1);
共计n+1个插入位置。
异常:
1) 插入位置i不合法:i > L.length 或 i < 0;
2) 顺序表已满,继续插入会造成溢出;
*算法分析
考虑顺序表中元素的平均移动次数AMN(一共n+1个位置可插入):
A
M
N
=
∑
i
=
1
n
+
1
P
i
M
i
AMN = \sum_{i=1}^{n+1} P_{i}M_{i}
AMN=∑i=1n+1PiMi
其中
P
i
P_{i}
Pi为第
i
i
i个位置被插入的概率;
M
i
M_{i}
Mi为插入第
i
i
i个位置时元素需要移动的次数;
∵
\because
∵ 顺序表中各个元素被查找的概率相等,
P
i
=
1
n
+
1
P_{i}=\frac{1}{n+1}
Pi=n+11,
M
i
=
n
+
1
−
i
M_{i}=n+1-i
Mi=n+1−i;
∴
\therefore
∴
A
M
N
=
∑
i
=
1
n
+
1
n
+
1
−
i
n
+
1
=
n
2
AMN = \sum_{i=1}^{n+1}\frac{n+1-i}{n+1}=\frac{n}{2}
AMN=∑i=1n+1n+1n+1−i=2n
∴
\therefore
∴
T
(
n
)
=
O
(
n
)
T(n) = O(n)
T(n)=O(n)
3.9 删除顺序表 L L L中的元素:
算法思想:
Step.1 判断插删除位置
i
i
i是否合法 ,
i
∈
[
1
,
n
]
i\in [1,n]
i∈[1,n];
Step.2 将欲删除的元素保留在
e
e
e中;
Step.3 将第
i
+
1
i+1
i+1至第n位的元素依次前移一个位置;
Step.4 表长L.length-1,返回OK。
//顺序表的删除
//删除顺序表中的第i个位置的元素并用e返回
status SqListDelete(SqList &L, int i, ElemType &e){
if(i<1||i>L.length)
return ERROR; //删除位置i不在[1,n]中,返回ERROR
if(L.length == 0)
return ERROR; //空表,无法删除元素
e = L.elem[i - 1]; //将欲删除的值用e返回
for (int j = i - 1; j < L.length;j++){
L.elem[j - 1] = L.elem[j]; //第i到第n个元素依次前移
}
L.length--; //表长减1
return OK;
}
*算法分析
考虑顺序表中元素的平均移动次数AMN(一共n个位置可删除元素):
A
M
N
=
∑
i
=
1
n
P
i
M
i
AMN = \sum_{i=1}^{n} P_{i}M_{i}
AMN=∑i=1nPiMi
其中
P
i
P_{i}
Pi为第
i
i
i个位置的元素被删除的概率;
M
i
M_{i}
Mi为删除第
i
i
i个位置时元素需要移动的次数;
∵
\because
∵ 顺序表中各个元素被查找的概率相等,
P
i
=
1
n
P_{i}=\frac{1}{n}
Pi=n1,
M
i
=
n
−
i
M_{i}=n-i
Mi=n−i;
∴
\therefore
∴
A
M
N
=
∑
i
=
1
n
n
−
i
n
=
1
2
(
n
−
1
)
AMN = \sum_{i=1}^{n}\frac{n-i}{n}=\frac{1}{2}(n-1)
AMN=∑i=1nnn−i=21(n−1)
∴
\therefore
∴
T
(
n
)
=
O
(
n
)
T(n) = O(n)
T(n)=O(n)