一、顺序表的概念
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。
二、顺序表的操作
1、顺序表的分类
①静态顺序表:使用定长数组存储
// 顺序表的静态存储
#define N 100
typedef int SLDataType;
typedef struct SeqList
{
SLDataType array[N]; // 定长数组
size_t size; // 有效数据的个数
}SeqList;
②动态顺序表:使用动态开辟的数组存储。
// 顺序表的动态存储
typedef struct SeqList
{
SLDataType* array; // 指向动态开辟的数组
size_t size ; // 有效数据个数
size_t capicity ; // 容量空间的大小
}SeqList;
2、顺序表的创建
void SeqListInit(SeqList *seqlist, int capacity){
//在堆上分配顺序表的空间
//初始化容量、size字段
assert(seqlist != NULL);//断言,不为空
seqlist->array = (SLDataType *)malloc(sizeof(SLDataType)*capacity);
seqlist->capacity = capacity;
seqlist->size = 0;
}
3、顺序表的销毁
void SeqLiatDestory(SeqList *seqlist){
//释放结构体的存储空间e *)malloc(sizeof(SLDataType)*capacity);
seqlist->capacity = 0;
seqlist->size = 0;
//额外的工作,把字段reset为初始值
assert(seqlist != NULL);
assert(seqlist->array != NULL);
free(seqlist->array);
seqlist->array = NULL;
seqlist->capacity = seqlist->size = 0;
}
4、顺序表的插入
①此处先不考虑空间不够的情况
头插:i有两种情况,注释的为i代表空间下标的情况,下面的为i代表数据下标的情况
在这里解释一下数据下标和空间下标:数据下标表示未移动前数据的下标,空间下标表示移动后数据的下标,在顺序表的插入过程中 数据下标 = 空间下标 - 1;空间下标 = 数据下标+1
void SeqListPushFront(SeqList * seqlist, SLDataType v){
//移动数据
//for (int i = seqlist->size; i >= 1; i++)//(i为空间)
//{
// seqlist->array[i] = seqlist->array[i - 1];
//}
for (int i = seqlist->size - 1; i >= 0; i--)//(i为数据,此处移动时从后往前移动,所以i--)
{
seqlist->array[i+1] = seqlist->array[i];
}
seqlist->array[0] = v;
seqlist->size++;
}
尾插
void SeqListPushBack(SeqList *seqlist, SLDataType v){
seqlist->array[seqlist->size] = v;
seqlist->size++;
}
中间插入
void SeqListPushInsert(SeqList *seqlist, SLDataType pos, SLDataType v)
{
for (int i = seqlist->size; i > pos; i--)//i为空间,pos为下标
{
seqlist->array[i] = seqlist->array[i - 1];
}
seqlist->array[pos] = v;
seqlist->size++;
#if 0
for (int i = seqlist->size - 1; i >= pos; i--)//i为数据
{
seqlist->array[i + 1] = seqlist->[i];
}
seqlist->array[pos] = v;
seqlist->size++;
#endif
}
②:当空间不够时需要扩容
扩容的三个步骤
a.申请新空间,一般申请为原空间的2 倍
b.把旧空间的内容拷贝到新空间
c.释放旧空间
static void CheclCapacity(SeqList *seqlist)
{
if (seqlist->size < seqlist->capacity)
{
return;
}
//需要扩容的情况
//申请新空间
int newCapacity = 2 * seqlist->capacity;
SLDataType *newArray = (SLDataType *)malloc(sizeof(SLDataType)* newCapacity);
//copy老数据到新空间;
for (int i = 0; i < seqlist->size; i++)
{
newArray[i] = seqlist->array[i];
}
//释放老空间,把新空间绑定到顺序表结构体
free(seqlist->array);
seqlist->array = newArray;
//更新容量
seqlist->capacity = newCapacity;
}
一般把扩容函数修饰为静态函数,static 修饰函数更改链接属性,从外部链接属性改编为内部链接属性,检查数据插入时,是否需要扩容,如果需要,则扩容。
5、顺序表的删除
头删:注释的为i代表数据下标的情况,空间下标与数据下标的关系前面已经讲过,此处移动数据时从前往后,则i++,顺序表的删除只是用后一个数据覆盖掉前一个数据就可以了
void SeqListPopFront(SeqList *seqlist)
{
assert(seqlist != NULL);
assert(seqlist->size > 0);
for (int i = 0; i < seqlist->size - 1; i++)//i为空间的下标
{
seqlist->array[i] = seqlist->array[i + 1];
}
//for (int i = 1; i <= seqlist->size - 1; i++)
//{
// seqlist->array[i - 1] = seqlist->array[i];//i为数据的下标
//}
//seqlist->size--;
}
尾删:后面没有数据覆盖它,它是最后一个数据,删除只是简单的把有效元素size减1即可
void SeqListPopBack(SeqList *seqlist)
{
assert(seqlist != NULL);
assert(seqlist->size > 0);
seqlist->size--;
}
中间位置删除:删除pos位置的数据
void SeqListErase(SeqList *seqlist, SLDataType pos){
assert(seqlist != NULL);
assert(seqlist->size > 0);
assert(pos >= 0 && pos <= seqlist->size - 1);
for(int i = pos; i < seqlist->size - 1; i++)//i为空间
{
seqlist->array[i] = seqlist->array[i + 1];
}
for (int i = pos + 1; i < seqlist->size; i++)//i为数据下标
{
seqlist->array[i - 1] = seqlist->array[i];
}
seqlist->size--;
}
删除第一次遇到的V
void SeqListRemove(SeqList* seqlist, SLDataType v)
{
int pos = SeqListFind(seqlist, v);
if (pos = -1)
{
return;
}
SeqListErase(seqlist, pos);
}
删除遇到的所有V
void SeqListRemoveAll(SeqList *seqlist, SLDataType v)
{
int i, j = 0;
for (i = 0, j = 0; i < seqlist->size; i++)
{
if (seqlist->array[i] != v)
{
seqlist->array[j] = seqlist->array[i];
j++;
}
}
seqlist->size = j;
}