前言
顺序表是一种参见的数据结构
特点:类似数组的储存,需要预先开辟空间已备数据插入。(1)同时顺序表也因此会存在和数组同样的问题(例如:越界)(2)同时因为其类似与数组的结构给二分查找这种算法提供了编译环境。
使用顺序表实现增删查改是需要注意:
1.整个顺序表有没有提前开辟空间。
2.空间是否足够不够,是否需要扩容。(一般扩容扩2倍)
一、顺序表基本定义方式
1.顺序表静态开辟版本
静态顺序表版本
typedef struct SepList
{
SlDataType a[N];
int size; //数组中存储数据
}List;
2.顺序表动态开辟版本
顺序表的动态开辟版本
typedef struct SepList
{
SlDataType* a;//动态开辟
int size; //数组中存储数据
int capacity; //数组实际能存数组的数据的空间容量
}List;
二、顺序表动态开辟内存
顺序表动态内存开辟
void SepListCheckCapacity(List* ps)
{
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SlDataType* temp = (SlDataType*)realloc(ps->a, newcapacity * sizeof(SlDataType));
if (temp == NULL)
{
printf("realloc fail \n");
exit(-1);
}
ps->a = temp;
ps->capacity = newcapacity;
}
}
三、增删查改的实现
void SepListPushBack(List* ps, SlDataType x)//尾插
{
//没有空间(增容)
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps -> capacity * 2;
SlDataType* temp = (SlDataType*)realloc(ps->a, newcapacity * sizeof(SlDataType));
if (temp == NULL)
{
printf("realloc fail \n");
exit(-1);
}
ps->a = temp;
ps->capacity = newcapacity;
}
//空间足够
ps->a[ps->size] = x;
ps->size++;
}
void SepListPopBack(List* ps)//尾删
{
if (ps->size > 0)
{
ps->a[ps->size - 1] = 0;
ps->size--;
}
}
void SepListPushFront(List* ps, SlDataType x)//头插
{
//增加内容容易出现越界现象(检查空间是否足够很重要,越界编译器不会报错)可以单独创建一个扩容函数,
//没有空间(增容)
/*if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SlDataType* temp = (SlDataType*)realloc(ps->a, newcapacity * sizeof(SlDataType));
if (temp == NULL)
{
printf("realloc fail \n");
exit(-1);
}
ps->a = temp;
ps->capacity = newcapacity;
}*/
SepListCheckCapacity(ps);//调用扩容函数版本 (尾插是尾插内部写入函数的版本)
//空间足够
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[0] = x;
ps->size++;
}
void SepListPopFront(List* ps)//头删
{
assert(ps->size > 0);
//挪动数据
int begin = 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
int SepListFind(List* ps, SlDataType x)//查找 (找到了返回下标,没找到返回-1)
{
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
void SepListInsert(List* ps, int pos, SlDataType x)
{
assert(pos <= ps->size && pos >= 0);
SepListCheckCapacity(ps);
int end = ps->size - 1;
//挪动数据
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[pos] = x;
ps->size++;
}
void SepListErase(List* ps, int pos)//删除指定位置数据
{
assert(pos < ps->size && pos >= 0);
//挪动数据
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
部分细节讲解:
(1)在自行编写代码的过程中在进行插入、删除操作时所有涉及的指针做参数的时候都记得用assert进行断言(让代码更加健壮)
(2)在顺序表中删除数据最为主要的方法是挪动数据对要删除数据进行覆盖,同时在插入数据特别是数据中间和数据头部插入时都需要进行数据挪动
(3)在顺序表中能够使用下标时顺序的一份优势可以好好利用下标进行查找。
总结
在整个顺序表中各种查找、扩容、删除、插入都大同小异注意举一反三。