目录
数据结构在数据元素关系上分为一对一的关系,一对多的关系和多对多的关系,在存储上分为顺序存储和非顺序存储。那么,把一对一关系和顺序存储组合起来,就是本文要探讨的顺序表。
1 定义
顺序表,就是顺序存储的线性表。
线性表,即为一对一关系。
顺序存储,也就是在相邻元素在虚拟内存的存储位置是相邻的。
所以顺序表在虚拟内存中差不多是这样子的
下面用代码来实现顺序表,从单个数据元素的来看,并没有什么特别,所以可以像定义变量一样定义单个数据元素,加上是顺序存储,所以可以一步到位用数组(data[])来实现就行,由于定义数组的时候一定要定义数组能存储多少个变量(如 data[3],即为数组 data 能存储 3 个元素),所以需要提前定义顺序表能够存储的数据元素的数量(MAX_SIZE),最后作为线性表还需要加上长度属性(length)。
1.1 代码实现
#define MAX_SIZE 10 // 顺序表最多存储 10 个元素
typedef struct {
ElemType data[MAX_SIZE]; // 存储单个数据元素,ElemType 为数据类型(即 int、char、bool 等
int length; // 顺序表的长度
} SqList;
2 顺序表的基本操作
顺序表的基本操作包括初始化和增删改查。
2.1 顺序表的初始化
2.1.1 代码实现
#define MAX_SIZE 10
/**
定义一个 int 类型的顺序表
**/
typedef struct {
int data[MAX_SIZE];
int length;
} SqList;
/**
顺序表的初始化
**/
void InitSqList(SqList &S) {
for (int i = 0; i < MAX_SIZE; i++)
S.data[i] = 0; // 将所有数据元素初始化为 0
S.length = 0; // 顺序表的初始长度为 0
}
2.1.2 时间复杂度分析
初始化操作中有一个循环结构,所以直接看循环结构就行。(什么是时间复杂度)
for (int i = 0; i < MAX_SIZE; i++)
S.data[i] = 0;
因为 MAX_SIZE 是一个常数,所以它的时间复杂度为 O(1)。
2.2 添加操作
首先需要判断顺序表是否已满,满的话即不能添加。其次需要判断插入的位置是否超出添加后顺序表长度(length + 1)或不存在,是的话也不能添加。前面两个条件都通过了,才能把添加位置后的元素向后挪动,给指定位置腾出空间,最后将新元素添加到指定位置。
2.2.1 代码实现
bool ListInsert(SqList &S,int i,int e) { // S 为原顺序表,i 为指定位置,e 为数据
if (S,length == MAX_SIZE) // 顺序表已满,无法添加
return false;
if (i < 0 || i > S.length + 1) // 指定位置不存在或超出顺序表范围
return false;
for (int j = S.length; j >= i; j--) // 指定位置后的元素向后挪动
S.data[j] = S.data[j - 1];
S.data[i - 1] = e; // 在指定位置添加数据
S,length++;
return true; // 添加操作成功
}
2.2.2 时间复杂度分析
添加操作有一个循环结构,直接看循环结构。
for (int j = S.length; j >= i; j--)
S.data[j] = S.data[j - 1];
因为这个循环是从结尾(S.length)开始到要添加的位置(i)结束的,假设这个长度为 n,也就是说需要循环 n 次,所以时间复杂度为 O(n)。
2.3 删除操作
首先需要判断删除位置是否存在或超出顺序表长度,判断成功后,需要让删除位置后的元素向前挪动,最后长度缩小一个长度(length - 1)。
2.3.1 代码实现
bool ListDelete(SqList &S,int i) { // S 为原顺序表,i 为删除的位置
if (i < 0 || i > S.length) // 判断删除位置是否存在
return false;
for (int j = i; j < S.length; j++) // 删除位置后的元素向前挪动
S.data[j - 1] = S.data[j];
S.length--; // 长度缩小一个单位
return true;
}
2.3.2 时间复杂度分析
删除操作有一个循环结构,直接看循环结构。
for (int j = i; j < S.length; j++)
S.data[j - 1] = S.data[j];
因为这个循环是从删除位置(i)开始到顺序表结尾(S,length)结束的,假设这个长度为 n,也就是说需要循环 n 次,所以时间复杂度为 O(n)。
2.4 查找操作
顺序表的查找操作包括按位查找和按值查找。
2.4.1 按位查找
通过元素的所在的下标(第几个元素)来查找。
2.4.1.1 代码实现
int GetElem(SqList S,int i) { // S 为顺序表,i 为第 i 个元素
return S.data[i - 1];
}
2.4.1.2 时间复杂度分析:
按位查找明显是一步到位的,所以时间复杂度为 O(1)。
2.4.2 按值查找
通过元素的值来查找。
2.4.2.1 代码实现
int LocateElem(SqList S,int e) { // S 为顺序表,e 为要查找的值
for (int i = 0; i < S.length; i++)
if (S.data[i] == e)
return i + 1; // 查找到则返回元素的位置
return -1; // 找不到则返回 -1
}
2.4.2.2 时间复杂度分析:
按值查找有一个循环结构,直接来看循环结构。按值查找是从第一个元素开始通过配置每个元素的值与要查找的值是否相同来查找的,假设从第一个元素到要查找的值的长度为 n,也就是要循环 n 次,所以时间复杂度为 O(n)。