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