文章目录
顺序表的概念及结构
顺序表是用一段 物理地址连续 的存储单元存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可以分为:
1.静态顺序表:使用定长数组存储。
2.动态顺序表:使用动态开辟的数组存储。
静态 & 动态顺序表的特点
静态顺序表:使用定长数组存储数据,也就意味着存储的数据量是固定的,当需要存储的数据量很少时,会造成空间浪费;当存储的数据量很大时,可能空间会不够;
#define MAX 100
typedef int SLDataType;
typedef struct SeqList
{
SLDataType a[MAX];//定长数组存储数据
size_t size;//数据个数
size_t capacity;//空间容量
}SeqList;
动态顺序表:使用动态开辟的数组来存储数据,当空间不够时,便扩容。
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* a;//动态开辟数组存储数据
size_t size;//数据个数
size_t capacity;//顺序表容量
}SeqList;
由于动态顺序表能够扩容的优点,之后的顺序表实现,都统一使用动态结构。
动态顺序表增删查改的接口函数
1.初始化顺序表
注意:
函数的参数必须使用指针形式,因为只有传址传递才能间接修改实参
//顺序表的初始化
void SeqListInit(SeqList* psl)
{
assert(psl);//使用断言防止psl为空指针
//因为空指针解引用程序将会崩溃
psl->a = NULL;
psl->capacity = psl->size = 0;
}
SeqList sl;
SeqListInit(&sl);
2.销毁顺序表
1.销毁顺序表时,必须将动态申请的空间进行释放,否则将会出现内存泄漏。
2.并且把指向那段空间的指针置为空指针,否则它将会是一个野指针。
//销毁顺序表
void SeqListDestory(SeqList* psl)
{
assert(psl);//使用断言防止psl为空指针
free(psl->a);//1
psl->a = NULL;//2
psl->capacity = psl->size = 0;
}
3.打印顺序表
//打印顺序表
void SeqListPrint(SeqList* psl)
{
assert(psl && psl->a);
for (int i = 0; i < psl->size; i++)
{
printf("%d ", psl->a[i]);
}
printf("\n");
}
4. 顺序表尾部插入数据
在进行插入数据前,需要先判断空间是否有剩余,如果空间满了,则需要扩容。
由于在初始化中并没有给定数组一块起始空间,所以在一开始插入数据时就需要进行扩容,并且需要给定一个起始空间大小。在扩容时,我们选择对原有空间翻二倍。
//顺序表尾部插入数据
void SeqListPushBack(SeqList* psl, SLDataType val)
{
assert(psl);
//判断是否需要扩容
check_capacity(psl);
//尾部插入数据
psl->a[psl->size] = val;
psl->size++;
}
一些问题的思考
为什么选择扩容二倍?
因为二倍是刚好合适的倍数,如果扩小了,那么当插入数据很多时,就要频繁的扩容,会导致性能的下降;如果扩大了,那么当插入数据不是很多时,可能造成空间的浪费。
既然有了扩容,那是否需要缩容
在c++库的顺序表中是没有缩容这个操作的,原因有二:
1.缩容这一操作是利用realloc来实现的,当需要缩容时,可能会在内存中重新寻找一块空间再将数据拷贝到那块空间上来实现缩容,这一操作是会损耗性能的
2.当需要重新插入数据时,又可能需要扩容,扩容时也有可能需要开辟一块新的空间,释放旧空间,这一操作也是需要消耗性能的。
5. 对空间容量的检查
//对空间容量的检查
void check_capacity(SeqList* psl)
{
assert(psl);
if (psl->capacity == psl->size)
{
int new_capacity = psl->capacity == 0 ? 4 : 2 * psl->capacity;
//如果起始容量为0,那么就给定一个初始值
//如果不为0,则将原有容量翻倍
//扩容
SLDataType* tmp = (SLDataType*)realloc(psl->a, new_capacity * sizeof(SLDataType));
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);//扩容失败,终止程序
}
//扩容成功
psl->a = tmp;
psl->capacity = new_capacity;
}
}
6.顺序表头部插入数据
如果要在顺序表头部插入数据,则需要从后往前依次挪动数据,将头部位置空出来。
//顺序表头部插入数据
void SeqListPushFront(SeqList* psl, SLDataType val)
{
assert(psl);
check_capacity(psl);//检查容量
//从后往前挪动数据
int end =