
🌈这里是say-fall分享,感兴趣欢迎三连与评论区留言
🔥专栏:《C语言从零开始到精通》、《C语言编程实战》、《数据结构与算法》
💪格言:做好你自己,你才能吸引更多人,并与他们共赢,这才是你最好的成长方式。
前言:
到目前为止C语言所有知识点我们已经学习完了,有一些不常用的知识点这里就不再继续拓展,从这篇博客开始,我们正式走入一个新的专栏:《数据结构与算法》,本篇我们就来了解一下:顺序表(本顺序表基于C语言)
文章目录
- 前言:
- 正文:
-
-
- 什么是顺序表?
-
- 顺序表分类
-
- 2.1 静态顺序表
- 2.2 动态顺序表
-
- 动态顺序表的实现
-
- 3.1 头文件
- 3.2 源文件(函数)
- 3.3 源文件(测试)
-
正文:
1. 什么是顺序表?
在数据结构中,顺序表是一种线性表的存储结构,它通过连续的存储空间来存储元素,使得表中的元素在逻辑上的相邻关系在物理存储位置上也相邻。
- 核心特点:
- 物理存储连续所有元素依次存放在一片连续的内存空间中,例如数组就是典型的顺序表实现。
- 元素按索引访问由于存储连续,可通过首地址 + 索引偏移量直接计算任意元素的地址,因此支持随机访问(时间复杂度为 O (1))。
- 容量固定或动态扩容
静态顺序表:初始化时确定容量,后续不可更改(易溢出)。
动态顺序表:容量可随元素数量增长自动扩容(如复制旧数据到新的更大空间)。
2. 顺序表分类
2.1 静态顺序表
定义:
c
#define num 100 //num是改变数组大小的,但只能使用前改变
struct StaticSeqList
{
int arr[num];//这里是一个定长数组
int size;//size用来记录已有的元素个数
};
总的来说静态顺序表其实和一般数组是很类似的
2.2 动态顺序表
定义:
c
struct seqlist
{
sltype* arr;//这是一个指针,可以开辟空间
int size;//记录已有元素个数
int capacity;//用来记录开辟空间长度
};
与静态顺序表不同,动态顺序表实际上和柔性数组一样,可控制数组大小
3. 动态顺序表的实现
下面带大家写一下这个顺序表:
其中主要是包含函数源文件的写法
3.1 头文件
想要实现一个顺序表首先肯定得先定义他,这里我们在添加我们需要的头文件以后定义一个动态顺序表
c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int sltype;//为数组元素类型进行重定义,方便以后修改
typedef struct seqlist
{
sltype* arr;//这是一个指针,可以开辟空间
int size;//记录已有元素个数
int capacity;//用来记录开辟空间长度
}sl;//用sl这个名字对动态顺序表进行重定义
以下是我们需要进行的函数的声明,为了方便我们一般把他们放到头文件中
c
//检查空间
void SLcheckcapacity(sl* ps);
//顺序表的初始化
void SLinit(sl* ps);
//顺序表的销毁
void SLdestory(sl* ps);
//顺序表的尾插
void SLappend(sl* ps, sltype x);
//顺序表的打印
void printsl(sl* ps);
//顺序表的头插
void SLprepend(sl* ps, sltype x);
3.2 源文件(函数)
下面就是我们的函数定义了,这里以注释和插叙的方式给大家解释
c
#include"Seqlist.h"//将头文件放入源文件中
- 初始化
这里是对动态顺序表的初始化,在主函数中我们首先创建一个类型为sl的叫sl1的动态顺序表,然后对它进行初始化
c
//初始化试验
sl sl1;
SLinit(&sl1);
c
//顺序表初始化
void SLinit(sl* ps)
{
ps->arr = NULL;
ps->size = 0;
ps->capacity = 0;
}
有没有人想过这里传参为什么传地址而不是传值?
- 实际上传值的话他会在函数内部创建一份形参(动态顺序表的拷贝)而无法实际修改真实的动态顺序表,而且创建这些形参时也会产生额外的内存占用
- 动态顺序表内部的成员实际上都是依靠指针来访问的,直接传地址来修改更合理
- 销毁
这里我们先不对顺序表进行任何操作,我们先来试着销毁刚才已经初始化的顺序表
c
//顺序表的销毁
void SLdestory(sl* ps)
{
assert(ps != NULL);//注意这里需要检查顺序表指针是否为空,使用assert宏来控制它
free(ps->arr);//在使用中我们会申请动态内存这个时候就需要释放内存
ps->arr = NULL;//释放内存以后将顺序表内的舒徐全部置空或者置零方式内存泄漏
ps->size = ps->capacity = 0;
}
- 插入操作_尾插
c
//顺序表的尾插
void SLappend(sl* ps ,sltype x)//这里定义时将地址传来,再将要插入的数据传入函数
{
assert(ps != NULL);
//插入之前,检查空间是否足够
SLcheckcapacity(ps);//检查空间我们紧跟尾插讲解
ps->arr[ps->size] = x;//确保没有问题以后将最后一个空间放上我们要插入的数据
ps->size++;//不要忘记同步实际使用空间
}
- 检查空间
检查空间大小时候我们考虑两种情况:①.还没有开辟空间;②已经开辟空间,但空间不够了需要扩容
c
//检查空间
void SLcheckcapacity(sl* ps)
{
assert(ps != NULL);
if (ps->capacity == ps->size)//实际使用的空间马上要超过总空间了,这个时候扩容
{
sltype newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
//定义一个新的实际空间准备使用
//这里用条件操作符如果为开辟空间就赋值为4,如果已有空间就翻倍
sltype* tmp = realloc(ps->arr, newcapacity * sizeof(sltype));
//防止是空指针,我们先将开辟的新地址给一个临时变量
if (tmp == NULL)//判断是否为空指针
{//如果是的话则报错
perror("realloc fail");
exit(1);
}
ps->arr = tmp;//检查完以后给原来的空间真正扩容
ps->capacity = newcapacity;//不要忘记同步实际空间数
}
}
- 顺序表打印
c
//打印顺序表
void printsl(sl* ps)
{
assert(ps != NULL);//每个函数使用前为防止传入空指针都要检查是否为空
sltype i = 0;
for (i = 0;i < ps->size ;i++)
{
printf("%d ", ps->arr[i]);//用循环的方式将顺序表内容打印出来
}
printf("\n");//增加换行符保证结构清楚
}
- 头插
c
//顺序表的头插
void SLprepend(sl* ps, sltype x)
{
assert(ps != NULL);
//先判断空间
SLcheckcapacity(ps);
//已有元素向后移动
int i = 0;
for (i = ps->size;i > 0 ; i--)//注意,为防止数据覆盖,这里是后面的元素先移动
{
ps->arr[i] = ps->arr[i-1];//最后一次移动是[0]的元素移动到[1]
}
ps->arr[0] = x;//将元素放入首个位置
ps->size++;//size增加1
}
- 头删
实际上头删和尾插是很类似的,都需要移动元素,保证顺序表的线性结构
c
//顺序表的头删
void delete_head(sl* ps) //删除只需要传入指针地址就好了
{
assert(ps != NULL);
assert(ps->size);//在保证数据表不为0时候才能删除
int i = 1;
for (i = 1;i <= ps->size ; i++)//注意这里i一开始需要是1,因为第一个位置是0
//头删在移动数据的过程中不用担心数据覆盖所以可以直接移动
{
ps->arr[i - 1] = ps->arr[i];//最后只需要移动size次就好了
}
ps->size--;//删除以后size--
}
- 尾删
c
//顺序表的尾删
void delete_tail(sl* ps)
{
assert(ps);//这是一种上方的简便写法
assert(ps->size);//在保证数据表不为0时候才能删除
ps->size--;
}
3.3 源文件(测试)
c
#include"Seqlist.h"
int main()
{
//初始化试验
sl sl1;
SLinit(&sl1);
//尾插试验
SLappend(&sl1,5);
printsl(&sl1);
//头插试验
SLprepend(&sl1, 4);
printsl(&sl1);
SLprepend(&sl1, 3);
printsl(&sl1);
SLprepend(&sl1, 2);
printsl(&sl1);
SLprepend(&sl1, 1);
printsl(&sl1);
//头删试验
delete_head(&sl1);
printsl(&sl1);
//尾删试验
delete_tail(&sl1);
printsl(&sl1);
//尾插试验
SLappend(&sl1, 0);
printsl(&sl1);
//销毁试验
SLdestory(&sl1);
return 0;
}
下面是测试结果:

- 以上就是顺序表的全部实现方法了
完整代码放在gitee仓库,有需要请自行查看
- 本节完...