2.2 顺序表
顺序表
定义
用顺序存储的方式实现线性表的顺序存储。把逻辑上相邻的元素存储在物理位置也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。
顺序表这种物理位置相邻的数据结构是基于数组实现的。数据结构作用的数据元素是数组中的元素,而数组是数据对象,故而对顺序表的操作,即是对单个数组进行操作。这也是与其他线性表不同的一个方面。
按顺序表的长度是否可变,分为静态顺序表和动态顺序表。
静态顺序表
定义
内存空间始终固定不变的顺序表。
下面的代码中类似
int &n
的代码; 的的语句属于 C++, C语言不能编译。
含义为:声明 n 为一个 int 类型的引用 。
如函数调用中将 实参 a 传给 n, 表示 n 为 a 的引用,或者说 a 的别名,在函数中将直接操作 a 中的内容。
#define MAXSIZE 10 // 顺序表最大长度
typedef struct { // 定义顺序表元素结构,并设置别名
int data[MAXSIZE] // 用静态数组来存放数据元素
int length; // 顺序表当前长度
} SeqList; // Sequence:顺序
基本操作及时间复杂度
1、初始化
初始化要遍历整个数组,循环次数为数组长度n,故时间复杂度为 O(n)。
// 1、初始化
void InitSeqList(SeqList &L){
// 随机分配的内存空间都存在垃圾数据,清空所有垃圾数据。
for(int i=0; i<MAXSIZE; i++){
L.data[i] = 0;
}
// length中也存有垃圾值,只有static全局变量分配到的内存才会是 0
L.length = 0; // 顺序表的长度初始为 0;
}
2、插入
插入数据时要将后面的元素都向后移,最坏情况下要将数组所有元素向后移,循环次数为数组的长度n,故时间复杂度为 O(n)。
// 2、插入元素
bool ListInsert(SeqList &L, int i, int data){
// 检查位序i 是否正确。位序从1开始。
if (i<1 || i > L.length+1) return false;
// 检查数组是否装满
if (L.length >= MAXSIZE) return false;
// 将位序i上的元素及其后面的元素后移。数组下标从0开始,比位序小1。
for (int j=L.length; j>=i; j--){
L.data[j] = L.data[j-1];
}
// 在位序为 i 处插入元素, 表尾指针后移。
L.data[i-1] = data;
L.length ++;
return true;
}
3、删除
删除元素需要将后面的元素向前移。最坏的情况下需要移动数组中除第一个元素外的所有元素,循环次数为 n-1,时间复杂度为 O(n)
// 3、删除元素; 删除 i 处的
bool ListDelete(SeqList &L, int i, int &e){
// 检查位序i 是否正确
if (i<1 || i > L.length) return false;
// 将位序i 上的元素通过 e 传递出去。
e = L.data[i-1];
// 向前移动位序i后面的元素
for( ; i<L.length; i++){
L.data[i-1] = L.data[i];
}
// 表尾指针前移
L.length--;
return true;
}
4、按位查找
由于顺序表的各个数据元素在内存中连续存放,因此可以根据起始地址和数据元素大小立即找到第 i 个元素——“随机存取”特性。
时间复杂度为 O(1)。
// 4、按位查找
int ListLocated(SeqList &L, int i){
// 检查位序i 是否正确
if (i<1 || i > L.length) return 0;
// 返回位序为 i 的元素。
return L.data[i-1];
}
5、按值查找
按值查找需要对比每个元素,无法提前知道元素的位置,故而需要遍历整个数组。最坏情况下,遍历完数组所有元素,循环执行n次,时间复杂度为 O(n)。
// 5、按值查找
int ListFind(SeqList &L, int e){
// 按顺序遍历所有元素
for(int i=0; i<L.length; i++){
if (L.data[i] == e) // 找到元素,返回位序
return i+1;
}
return 0; // 遍历完所有元素仍未找到,返回0,而位序从1开始。
}
动态顺序表
定义
顺序表满了以后,如果再插入数据,就重新开辟一个更大的空间。
// 动态顺序表
#define InitSize 10 // 顺序表的初始长度
typedef struct{
int *data; // 指示动态分配数组的指针
int MaxSize; // 顺序表的最大容量
int length; // 顺序表的当前长度
} DySeqList; // dynamic: 动态的
基本操作
动态顺序表的初始化、与插入操作与静态顺序表稍有不同,且多一个增加数组长度的操作。其他操作均与静态数组一致。
1、初始化
数据初始化需要遍历整个数组,时间复杂度为 O(n)。
// 1、初始化
void InitSeqList(DySeqList &L){
// malloc 动态申请一块连续的内存空间
L.data = (int *)malloc(InitSize*sizeof(int));
L.length = 0;
L.MaxSize = InitSize;
// 动态分配的内存中也都是垃圾值,需要清空
for(int i=0; i<L.MaxSize; i++){
L.data[i] = 0;
}
}
2、增加数组长度
复制数据和清理数组需要遍历整个数组,时间复杂度为 O(n)。
// 2、增加动态数组的长度
void IncreaseSize(DySeqList &DL, int len){
// n 增加的长度
int *p = DL.data;
DL.MaxSize += len;
// 重新申请空间
DL.data = (int*)malloc(DL.MaxSize*sizeof(int));
// 复制数据
int i = 0;
for ( ; i<DL.length; i++){
DL.data[i] = p[i];
}
// 清除垃圾数据
for ( ; i<DL.MaxSize; i++){
DL.data[i] = 0;
}
// 释放旧内存
free(p);
}
3、插入
插入数据与静态顺序表不同,动态数组满了后可以增加数组长度,而不是返回 false。
最坏情况下,需要先增加数组长度,再插入元素, 时间复杂度都为 O(n),和仍然为 O(n)。
时间复杂度为 O(n)。
// 3、插入元素
bool ListInsert(DySeqList &L, int i, int data){
// 检查位序i 是否正确。位序从1开始。
if (i<1 || i > L.length+1) return false;
// 检查数组是否装满
if (L.length >= MAXSIZE)
IncreaseSize(L, InitSize);
// 将位序i上的元素及其后面的元素后移。数组下标从0开始,比位序小1。
for (int j=L.length; j>=i; j--){
L.data[j] = L.data[j-1];
}
// 在位序为 i 处插入元素, 表尾指针后移。
L.data[i-1] = data;
L.length ++;
return true;
}
4、删除
算法完全同静态顺序表,时间复杂度为 O(n)。
// 4、删除元素; 删除 i 处的
bool ListDelete(DySeqList &L, int i, int &e){
// 检查位序i 是否正确
if (i<1 || i > L.length) return false;
// 将位序i 上的元素通过 e 传递出去。
e = L.data[i-1];
// 向前移动位序i后面的元素
for( ; i<L.length; i++){
L.data[i-1] = L.data[i];
}
// 表尾指针前移
L.length--;
return true;
}
5、查找
查找算法完全同静态顺序表,按位查找、按值查找的时间复杂度分别为 O(1)、O(n)。
// 5、按位查找
int ListLocated(DySeqList &L, int i){
// 检查位序i 是否正确
if (i<1 || i > L.length) return 0;
// 返回位序为 i 的元素。
return L.data[i-1];
}
// 6、按值查找
int ListFind(DySeqList &L, int e){
// 按顺序遍历所有元素
for(int i=0; i<L.length; i++){
if (L.data[i] == e) // 找到元素,返回位序
return i+1;
}
return 0; // 遍历完所有元素仍未找到,返回0,而位序从1开始。
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix