顺序表
线性表
在此之前首先先介绍一下线性表的概念
1、线性表:是具有n个相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表,链表,队列,字符串等
2、线性表在逻辑上是线性结构,也就是连续的一条直线。但是在物理上存储时,通常是以数组和链式结构的形式进行存储。
顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般分为静态顺序表与动态顺序表
静态顺序表:
优点:如果空间满了就不让其插入
缺点:给多大的空间很难确定
typedef struct SeqList
{
SLDataType a[N];
int size;//表示数组中存储了多少个数据
}SL;
由于不好掌握多大空间所以我们一般采用动态顺序表
动态顺序表实现:
typedef struct SeqList
{
SLDataType* a;
int size;//表示数组中存储了多少个数据
int capacity;//数组的实际能存的空间容量是多大 是数据个数 不是字节
}SL;
接下来列出各函数的声明 讲解部分函数
void SeqListInit(SL* ps);//初始化 要传指针 形参的改变不会影响实参
void SeqListPushBack(SL* ps, SLDataType x);//尾插
void SeqListPopBack(SL* ps);//尾删
void SeqListPushFront(SL* ps, SLDataType x);//头插
void SeqListPopBack(SL* ps);//头删
void SeqListPrint(SL* ps);
void SeqListDestory(SL* ps);
void SeqListCheckCapacity(SL* ps);
int SeqListFind(SL* ps, SLDataType x);
//在任意位置插入
void SeqListInsert(SL* ps, int pos, SLDataType x);
//在指定位置删除数据
void SeqListErase(SL* ps, int pos);
初始化
void SeqListInit(SL* ps)
{
ps->a = NULL;
ps->size = ps->capacity = 0;
}
尾插
在尾部插入一个元素我们需要考虑三种情况
1、整个顺序表没有空间
2、空间不够需要扩容
3、空间足够直接插入数据
void SeqListPushBack(SL* ps, SLDataType x)
{
SeqListCheckCapacity(ps);//检查空间是否足够 不够扩容
ps->a[ps->size] = x;//在size位置直接放x
ps->size++;
}
检查空间
在尾插函数中我们使用了SeqListCheckCapacity()函数,在这里写出该函数的实现:
void SeqListCheckCapacity(SL* ps)
{
if (ps->size == ps->capacity)//如果没有空间或者空间不足,扩容
{
//1、直接就没有开辟空间
//2、满了
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
}
尾删
void SeqListPopBack(SL* ps)
{
//断言 条件为真 就没事
assert(ps->size > 0);
ps->size--;
//只有--不行 如果size=0以后 再size-- 就变成负数
}
头插
void SeqListPushFront(SL* ps, SLDataType x)
{
SeqListCheckCapacity(ps);
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[0] = x;
ps->size++;
}
头删
void SeqListPopFront(SL* ps)
{
//整体数据往前移动
// 1 2 3 4 5
//begin
//size --
assert(ps->size > 0);//有数据才删除
int begin = 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
查找指定元素并且返回该位置
int SeqListFind(SL* ps, SLDataType x)
{
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
任意有效位置处插入
void SeqListInsert(SL* ps, int pos, SLDataType x)
{
//首先pos位置必须有效
assert(pos>=0 && pos <= ps->size);//为假就报错
SeqListCheckCapacity(ps);
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[pos] = x;
ps->size++;
}
删除有效位置的数据
void SeqListErase(SL* ps, int pos)
{
assert(pos >= 0 && pos < ps->size);//ps->size处没有有效数据
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
打印该顺序表
void SeqListPrint(SL* ps)
{
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
销毁动态开辟的空间
void SeqListDestory(SL* ps)
{
free(ps->a);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
顺序表缺陷:
1、空间不够的时候需要增容,增容有代价
realloc扩容的时候是原地扩容(代价低)或者异地扩容(要开辟新的空间)
2、避免频繁扩容,我们选择了扩容二倍,这就可能导致一定的空间浪费
3、顺序表要求数据连续存储,从中间插入数据或者挪动数据,效率不高
以上就是顺序表的基本功能实现 如果需要详细代码可以访问个人gitee:https://gitee.com/Proteinzmm