目录
一. 顺序表分类
- 静态顺序表
- 动态顺序表
静态顺序表:使用定长数组存储元素
动态顺序表:使用动态开辟的数组存储
二. 接口实现
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪 费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实 现动态顺序表。
将要实现的功能:
基本的增删查改,包括:
初始化,检查空间,尾插,头插,尾删,头插,头删,查找,在指定位置插入,删除指定位置的值,销毁,打印
// 顺序表初始化
void SeqListInit(SeqList* psl, size_t capacity);
// 检查空间,如果满了,进行增容
void CheckCapacity(SeqList* psl);
// 顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x);
// 顺序表尾删
void SeqListPopBack(SeqList* psl);
// 顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x);
// 顺序表头删
void SeqListPopFront(SeqList* psl);
// 顺序表查找
int SeqListFind(SeqList* psl, SLDataType x);
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* psl, size_t pos);
// 顺序表销毁
void SeqListDestory(SeqList* psl);
// 顺序表打印
void SeqListPrint(SeqList* psl);
这是定义的结构体:
//重定义类型,方便更改
typedef int SLDataType
//动态顺序表的定义,为了方便后续使用,重定义
typedef struct SeqList
{
SLDataType* a;
int sz;
int capacity;
}SL, SeqList;
注:以下实现都进行了assert断言,因为传的指针不允许为空,不然毫无意义
1. 初始化顺序表
因为没什么注意事项,直接放代码
void SeqListInit(SeqList* psl)
{
assert(psl);//判空
psl->a = NULL;
psl->sz = 0;
psl->capacity = 0;
}
2. 顺序表尾部插入
值得注意的是要判断是否需要扩容
void SeqListPushBack(SeqList* psl, SLDataType x)
{
assert(psl);//判空
SeqListCheckcapacity(psl);//容量检查
//无论是否有增容,都要尾部插入
psl->a[psl->sz] = x;
psl->sz++;
}
3. 容量检查
需要注意,扩容时,如果初始容量是0,那就会出现问题,这种方法可以有效处理当初始容量设置初始容量为0时的情况
void SeqListCheckcapacity(SeqList* psl)
{
assert(psl);//判空
//增容
if (psl->sz == psl->capacity)
{
size_t newcapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
//为了防止初始化的值为零,不能直接乘以2,下面的扩容会失败
SLDataType* temp = (SLDataType*)realloc(psl->a, sizeof(SLDataType) * newcapacity);
if (temp == NULL)
{
printf("open failed\n");
exit(-1);//终结程序,不再运行
}
else
{
psl->a = temp;
psl->capacity = newcapacity;//将增容空间赋值给旧的容量
}
}
}
4. 删除顺序表尾部元素
需要注意下标一直自减会导致越界,需要处理越界问题
void SeqListPopBack(SeqList* psl)
{
assert(psl);//判空
if (psl->sz > 0)//必须加这个条件,不然一直减减有可能越界
{
psl->sz--;//尾删直接sz--,因为空间是可回收的,无需自己覆盖
}
}
5. 销毁顺序表
即释放空间并置0
void SeqListDestroy(SeqList* psl)
{
assert(psl);//判空
//释放并置空
free(psl->a);
psl->a = NULL;
//置0
psl->sz = 0;
psl->capacity = 0;
}
6.打印顺序表
没什么注意事项
void SeqListPrint(SeqList* psl)
{
assert(psl);//判空
int i = 0;
for (i = 0; i < psl->sz; i++)
{
printf("%d ", psl->a[i]);
}
printf("\n");
}
7. 顺序表头部插入
没什么特别注意的事项
void SeqListPushFront(SeqList* psl, SLDataType x)
{
assert(psl);//判空
SeqListCheckcapacity(psl);//容量检查
int end = psl->sz - 1;
while (end >= 0)//头部插入应该把数据往后赋值,0也在内
{
psl->a[end+1] = psl->a[end];
end--;
}
psl->a[0] = x;
psl->sz++;
}
8. 顺序表头部删除
需注意,下标不能一直自减下去,不然会导致越界
void SeqListPopFront(SeqList* psl)
{
assert(psl);//判空
int begin = 1;
if (psl->sz > 0)//必须加这个条件,不然一直减减有可能越界
{
while (begin < psl->sz)
{
psl->a[begin - 1] = psl->a[begin];
begin++;
}
psl->sz--;
}
}
9. 任意位置插入
1. 由于数组下标一律大于0,所以,根据C++里的实现,我们也应该设置无符号整型,而不是有符号整型
2. 要注意pos是否越界
3. 进行插入时,尤其要注意无符号整型的越界问题,可能导致死循环,或者有符号整型以及无符号整型一起使用导致了整形提升问题而出现的bug问题
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x)
{
assert(psl);//检查psl
//检查是否越界
if (pos > psl->sz)
{
printf("越界\n");
return;
}
//检查是否需要扩容
SeqListCheckcapacity(psl);
//下标使用无符号整形,end指向最后应该元素后面
size_t end = psl->sz;
//进行插入,这里有个注意事项,当除这种以外的情况
//如果无符号和有符号整形一起出现,要注意无符号整形和有符号整形在进行计算时会整形提升
//如果使用的全是无符号整形,那就要注意end别越界而导致死循环
while (end > pos)
{
psl->a[end] = psl->a[end - 1];
--end;
}
psl->a[pos] = x;
psl->sz++;
}
10. 删除指定位置的数据
注意别越界
void SeqListErase(SeqList* psl, size_t pos)
{
assert(psl);//判空
//判断越界
if (pos >= psl->sz)
{
printf("越界\n");
return;
}
size_t begin = pos + 1;
//开始移动
while (begin < psl->sz)
{
psl->a[begin - 1] = psl->a[begin];
++begin;
}
--psl->sz;
}
11. 查找指定的数据
没什么注意事项
void SeqListFind(SeqList* psl, SLDataType x)
{
assert(psl);//判空
//遍历寻找,返回下标
for (int i = 0; i < psl->sz; i++)
{
if (psl->a[i] == x)
{
return i;
}
}
//找不到返回-1错误码
return -1;
}