线性表是一个具有n个相同元素类型的有序系列。 常见的线性表有:顺序表,链表,队列,栈…
线性表的划定:在逻辑上是线性的,物理结构上可以不是连续的。
- 顺序表是一个线性表,在逻辑是线性的,物理结构上也是连续的。
1.顺序表可分为静态顺序表和动态顺序表
静态顺序表:数组元素个数是一个固定值,使用定长数组来存储数据
typedef struct SeList
{
int a[100];
int size;//记录有效数据个数
}SL;
动态顺表表:动态开辟空间来存放数据
typedef int SLDataType;
typedef struct SeList
{
SLDataType *a;
int size;//记录有效数据个数
int capacity;//记录开辟数组空间的总数
}SL;
顺序表的初始化,是现在测试函数中或主函数中创建一个结构体(顺序表)变量,封装一个函数进行初始化。
直接在测试函数中或主函数中创建位置处初始化也是可以。代码是灵活的。
封装一个函数以后以后实现初始化操作比较方便
这里来封装一个顺序表初始化函数
//想要改变外面函数的变量需要外面函数的地址才能修改,所以参数是一个(顺序表)结构体的指针
void SeListInit(SL* p)
{
p->a = NULL;//这里也可以直接开辟空间
p->size = p->capacity = 0;
}
void SeListPushBack(SL* p)
{
这里传过来的指针一定不能为空,因为要使用开辟的数组
assert(p);
//插入之前,判断够不够空间存入数据
if(p->size==p->capacity)
{
//这里要判断一下p->capacity的值是否为0,为0*2还是0,就没有办法开辟空间
int newcapacity = p->capacity==0?4:capacity*2;
SLDataType * temp = (SLDataType*)recalloc(sizeof(SLDataType)*newcapacity)
//判断是否为NULL
if(temp==NULL)
{
perror("recalloc faile");
}
//此时开辟空间成功,要接收开辟新开辟空间的指针赋值给结构体中的成员
SLDataType * a =temp;
temp = NULL;
p->capacity = newcapacity;
}
//此时有足够空间扩容
p->a[p->size++] = x;
顺序表的头插就是将每一个数据移动一位,这是第一个位置插入数据就不会影响顺序表的数据了。
头插操作,数组元素的后移
注意在结构体中定义的size初始值为0,在插入数据之后,他会到有效数据的下一位置。
void SeListFront(SL* p)
{
assert(p);
//判断是否需要扩容 这里的扩容操作不止一个函数使用
//把扩容操作封装一个函数,方便使用
SLCheckCapacity(p);
for(int i = p->size ; i>0 ; i--)
{
p->a[i-1]=p->[i];
}
}
扩容:
void SLCheckCapacity(SL* p)
{if(p->size==p->capacity)
{
//这里要判断一下p->capacity的值是否为0,为0*2还是0,就没有办法开辟空间
int newcapacity = p->capacity==0?4:capacity*2;
SLDataType * temp = (SLDataType*)recalloc(sizeof(SLDataType)*newcapacity)
//判断是否为NULL
if(temp==NULL)
{
perror("recalloc faile");
}
//此时开辟空间成功,要接收开辟新开辟空间的指针赋值给结构体中的成员
SLDataType * a =temp;
temp = NULL;
p->capacity = newcapacity;
}
}
尾删操作让顺序表不能访问就可以,直接p->size–,数据不要改变,也不可以字节释放掉,因为开辟的是一个数组空间,释放掉空间要把所以开辟好的空间全部释放。
void SeListPop(SL*p)
{
assert(p);
assert(p->size);
p->size--;
}
void SeListPopFront(SL*p)
{
assert(p);
assert(p->size);
for(int i = 0;i<p->size;i++)
{
p->a[i] = p->a[i+1];
}
p->size--;
}
void SLInsert(SL* ps, int pos, SLDataType x) {
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
for (size_t i = ps->size-1; i >=pos ; i--)
{
ps->a[i + 1] = ps->a[i];
}
ps->a[pos] = x;
ps->size++;
}
void SLErase(SL* ps, int pos) {
assert(ps);
assert(!SLISEmpty);
assert(pos >= 0 && pos < ps->size);
for (size_t i = pos; i <ps->size-1 ; i++)
{ // size-2 size-1
ps->a[i] = ps->a[i+1];
}
ps->size--;
}
void SeList(SL*p)
{
assert(p);
assert(p->a!=NULL);
free(p);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
顺序表的优点:可以下标访问,尾插效率高
缺点:头,中间节点处插入删除操作,效率不高,时间复杂度为O(N)。可能会存在空间浪费问题。