文章目录
顺序表
线性表
定义:
线性表是n个具有相同特性的数据元素的有限序列,常见的线性表有:顺序表、链表、栈、队列…
之所以叫线性表,是因为线性表在逻辑上是线性结构,好像一条线一样贯穿始终,但在物理结构上(即内存中的结构)并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
本篇文章讲解顺序表。
顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般采用数组存储,包括静态数组和动态数组。
静态顺序表
//创建一个静态顺序表
#define N 10 //重定义宏或者标识符
typedef int SLDataType;//重定义类型,以后要改的时候直接改这儿
struct SeqList{
SLDataType a[N];
int size;//可以用来存储数组的大小
}
静态顺序表实际上不好用,因为个数写死了,大了浪费空间,小了不够用,使用起来非常不方便,所以我们实际上通常使用动态顺序表。
动态顺序表
动态顺序表的特点是按需申请,非常灵活。
#define INIT_CAPACITY 10 //顺序表的初始长度
//创建一个动态顺序表
typedef int SLDataType;//重定义类型,以后要改的时候直接改这儿
struct SeqList{
SLDataType* a;
int size;//可以用来存储当前数据个数
int capacity;//用来存储当前空间的容量
}SL;
实现顺序表的一些功能(增删查改)
初始化顺序表
void SLInit(SL* ps){
ps->a=(SLDataType*)malloc(sizeof(SLDataType) * INIT_CAPACITY);//开辟初始大小个空间
if(ps->a==NULL){
perror("malloc failed");
return;
}
ps->size=0;
ps->capacity=INIT_CAPACITY;//变成初始大小了
}
销毁顺序表(因为动态申请一定要记得销毁否则内存泄漏)
void SLDestroy(SL* ps){
free(ps->a);
ps->a=NULL;//好习惯,置空
ps->size=0;
ps->capacity=0;
}
顺序表里增加数据
我们使用的是动态顺序表,因此在增加新数据之前要检查是否满容量,满容量需要扩容,扩容可以封装成一个函数:
void SLCheckCapacity(SeqList* ps) {
assert(ps);
SLDateType* tmp;
if (ps->size == ps->capacity) {
//注意扩容扩的是数组,是ps->a而不是ps,注意了
tmp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * ps->capacity * 2);
if (tmp == NULL) {
perror("realloc failed\n");
}
ps->a = tmp;//防范异地扩容
ps->capacity *= 2;//扩容以后容量标识变一下
}
}
尾插法(在数据末尾加数据):
void SLPushBack(SL* ps,SLDataType x){
assert(ps);
//检查扩容
SLCheckCapacity(SeqList* ps);
ps->a[ps->size++]=x;
}
头插法(在第一个数据之前添加新数据):
void SLPushFront(SeqList* ps, SLDateType num) {
assert(ps);
//检查扩容
SLCheckCapacity(ps);
//插入
int end = ps->size;
while (end > 0) {
ps->a[end] = ps->a[end - 1];
end--;
}
ps->a[end] = num;
ps->size++;//插入了要把数据个数更新一下啊
}
在下标为pos的位置上插入一个数:
void SeqListInsert(SeqList* ps, int pos,SLDateType num) {
assert(ps);
assert(pos >= 0 && pos <= ps->size);//检查参数,插入的时候可以尾插
SLCheckCapacity(ps);
int end = ps->size;
while (pos < end) {
ps->a[end] = ps->a[end - 1];
end--;
}
ps->a[pos] = num;
ps->size++;//数量增加了,记得加上
}
删除数据
尾删法:
void SLPopBack(SL* ps){
//ps->a[ps->size-1]=0;//现在最后一个下标是size-1,但万一数据本来就是0呢?就可能混淆,所以直接减ps->size比较合适,后面访问也是用size
//如果已经删空了就不要再删了,所以要检查
//温柔的检查,删空了就不删了,我也不终止程序,不过没有报错信息
if(ps->size == 0){
return;
}
//暴力的检查,断言发挥作用的时候会终止程序并显示报错信息
assert(ps->size > 0);
ps->size--;
return;
}
头删法:
void SLPopFront(SeqList* ps) {
assert(ps);
assert(ps->size > 0);
int end = 0;
while (end < ps->size) {
ps->a[end] = ps->a[end + 1];
end++;
}
ps->size--;
}
删除下标为pos的数据
void SeqListErase(SeqList* ps, int pos) {
assert(ps);
assert(pos >= 0 && pos < ps->size);//检查参数,但相当于尾删的时候删不了size位置的
assert(ps->size > 0 );
int end = pos;
while (end < ps->size) {
ps->a[end] = ps->a[end + 1];
end++;
}
ps->size--;//数量减少了
}
查找数据
查找某数据,并返回下标,如果找不到返回-1
int SeqListFind(SeqList* ps, SLDataType num) {
assert(ps);
int i = 0;
for (i = 0; i < ps->size; i++) {
if (ps->a[i] == num) {
return i;
}
}
return -1;//没有找到,返回-1
}
修改数据
void SeqListModify(SeqList* ps, SLDataType num,SLDataType newnum) {
assert(ps);
int pos = 0;
//先查找,以数据1为例
pos = SeqListFind(ps,1);
if(pos != -1){
ps->a[pos] = newnum;
}
}
打印数据
将顺序表中的数据打印出来
void SLPrint(SeqList* ps) {
assert(ps);
int i = 0;
for (i = 0; i < ps->size; i++) {
printf("%d ", ps->a[i]);
}
printf("\n");
}
本文结束。
(如果文章有错误之处,请在评论区点醒作者,谢谢!)