目录
概念
简而言之,顺序表就是数组,是由一段连续的物理空间组成的序列。
数组分为定长数组和变长数组,同理,顺序表也是有定长(静态)的和变长(动态)的,二者的区别就是前者的储存的空间是有限的,而后者的空间可以根据情况调整空间容量。
顺序表准备工作
typedef int SLDataType;
//定义
typedef struct SeqList
{
SLDataType* arr;//动态顺序表使用动态开辟可以扩大空间
//如果是静态的顺序表
//SLDataType arr[N];//N是确定的值
int capacity;
int size;
}SL;
1.数组中储存的元素可能是任意类型,为了更加方便,我们将类型定义为SLDataType。
2.在储存元素的过程中我们对数组进行操作,需要随时记录下标和元素的个数,所以将数组和size(元素的个数)和capacity(总容量)进行包装,方便进行访问。
函数接口
1.初始化
//初始化
void SLInit(SL* ps);
利用函数对形参进行值的修改,就需要传递地址,所以形参的类型应该是封装好的结构体指针类型。
//初始化
void SLInit(SL* ps)
{
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
刚开始时数组中应该没有元素,而且下标是0,个数和容量也是0,在插入的时候,在进行开辟空间。
2.尾插
//尾插
void SLPushBack(SL* ps,SLDataType x)
{
//assert(ps);
//空间不够,扩容
SLCheckCapacity(ps);//如果空间足够直接跳过,如果空间不够,就进行扩容;
//空间足够,直接插入
ps->arr[ps->size++] =x;
}
尾插就是按照顺序进行直接赋值,但是在插入前需要进行检查是否容量已经满了,如果容量已经满了那么就需要进行扩容,我们将扩容进行函数包装,以便在后续使用到。
void SLCheckCapacity(SL* ps)
{
if (ps->capacity == ps->size)
{
//记录的总空间翻倍
int newcapacity = (ps->capacity == 0) ? 4 : 2 * ps->capacity;
//顺序表的空间也翻倍
SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));
// / realloc的函数的参数是空间地址,新的空间地址,以字节为单位;
// 判断是否扩容成功
if (tmp == NULL)
{
perror("realloc fail!\n");
exit(1);
}
//将新的空间给到数组空间
ps->arr = tmp;
//将新的空间大小重新赋值
ps->capacity=newcapacity;
}
}
先来进行if判断,如果已有元素个数(size)等于容量,那么就需要进行扩容,第一次进行扩容时,由于初始化容量为0,所以我们默认开辟4个元素的空间,如果不是第一次进行开辟空间,我们直接将空间进行翻倍扩大,我们使用realloc来进行扩容,realloc是在原有的地址进行扩容。
注意:我们开辟的新空间需要先用一个临时变量进行接收,目的是防止开辟失败造成原有的空间损坏。
开辟过之后直接将临时变量进行赋值。
- 注意:capacity是元素个数的最大值,而tmp是元素个数最大值所占的空间,单位是字节;
3.头插
//头插
void SLPushFront(SL* ps,SLDataType x)
{
//判断是否满容
SLCheckCapacity(ps);
//全部向后移
for (int i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[0] = x;
ps->size++;
}
头插的时间复杂度是O(n),因为数组存储的地址是连续的,所以在头部插入,就需要将所有的元素进行后移, 同时也需要进行判断是否需要扩容。
4.尾删
//尾删
void SLPopBack(SL* ps)
{
//不能为空
assert(ps);
assert(ps->size);
ps->size--;
}
在尾删之前需要先判断是否是个空的数组,如果数组是空的就无法进行删除,而且传递过来的地址也不能为空,对于删除最后面的元素,我们直接将元素个数size-1即可。
5.头删
//头删
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->size);
for (int i = 0; i<ps->size-1; i++)
{
ps->arr[i] = ps->arr[i+1];
}
ps->size--;
}
同理,头删也得保证数组和地址不为空,对于头删,我们直接把第二个及后面的所有元素向前面移动即可。最后不要忘了size也要减1;
6. 在指定位置插入数据
//指定插入位置的数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
//插入的位置限制
assert(pos-1 >= 0 && pos-1 <= ps->size);
SLCheckCapacity(ps);
for (int i = ps->size; i >pos-1; i--)
{
ps->arr[i] = ps->arr[i-1];
}
ps->arr[pos-1] = x;
ps->size++;
}
pos是指的第pos个元素,需要先判断pos是否合法,pos-1需要大于0小于size;我们要实现在pos处插入数据,就需要将pos以及后面的所有数据进行后移,然后直接进行赋值,最后size加1;
7.在指定位置删除数据
//指定位置删除数据
void SLDelete(SL* ps, int pos)
{
assert(ps);
assert((pos-1) >= 0 && (pos-1) < ps->size);
//pos后面的数据向前移动
for (int i = pos-1; i <ps->size-1; i--)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
同理,保证ps和pos的合法性,再pos删除数据,就要将后面的数据全部向前移动一位,最后size--;
8.查找
//查找
void search(SL* ps, int pos)
{
assert(ps);
assert(pos-1 >= 0 && pos-1 < ps->size);
printf("这个位置上的数是%d", ps->arr[pos - 1]);
}
直接打印下表是pos-1的数据即可;
9.修改
//修改
void change(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos-1 >= 0 && pos-1 < ps->size);
{
ps->arr[pos - 1] = x;
}
}
修改就是在改位置直接进行赋值即可。
10.打印
//打印
void SLPrint(SL* ps)
{
printf("操作后顺序表为:>");
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->arr[i]);
}
printf("\n");
}
时间复杂度是O(N),将整个数组进行遍历,按顺序打印;
11.销毁
void SLDestory(SL*p)
{
assert(p);
free(arr);
arr=NULL;
p->size=0;
p->capacity=0;
}
将原有的空间进行释放,size和capacity置为0;arr置为空;