一、顺序表的分类(以存放整形数据为例)
1.静态顺序表
typedef struct SeqList
{
int arr[100];
int size;//记录顺序表有效数据个数
}SL;
2.动态顺序表
typedef struct SeqList
{
int* arr;//用于开辟合适空间大小的数组来存放数据
int size;//记录顺序表有效数据个数
int capaciy;//记录顺序表空间大小
}SL;
3.两种顺序表的比较
静态顺序表与动态顺序表的区别主要是静态顺序表是用一个固定大小的数组来储存数据的,而动态顺序表是通过动态开辟一个大小的空间来存储数据的,它的大小是可变的,因此动态顺序表跟适合应用到项目中,因为在具体生活中,数据是会不断增加的,因此用来存储数据的空间也需要不断增加,而动态顺序表可以做到改变空间的大小
二、动态顺序表代码实现
顺序表的底层就是数组,但是与数组不同的是,顺序表封装了一系列函数,使它具有增、删、查、改的功能,现在,我们就需要通过代码来实现顺序表的这些功能。
用于实现顺序表的代码较多,这里分别使用了一个头文件SeqList.h(用于创建顺序表,以及对顺序表的函数进行声明)、两个源文件SeqList.c(用于函数的实现),test.c(用于代码的测试),
下面我们先将头文件的代码展示出来,
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//定义顺序表结构和声明顺序表的函数
typedef int SLDataType;
//顺序表不一定存储整形数据,也有可能是其它类型,因此用
//tppedef进行类型的重命名,下次如需要修改只需将int改为
//其它类型即可
//1.定义顺序表结构
typedef struct SeqList
{
SLDataType* arr;
int size;//有效数据个数
int capacity;//顺序表的空间大小
}SL;
//2.顺序表初始化
void SLInit(SL* ps);
//4.尾部插入
void SLPushBack(SL* ps, SLDataType x);
//5.头部插入
void SLPushFront(SL* ps, SLDataType x);
//6.尾部删除
void SLPopBack(SL* ps);
//7.头部删除
void SLPopFront(SL* ps);
//3.顺序表的销毁
void SLDestroy(SL* ps);
//顺序表的打印
void SLPrint(SL s);
通过头文件的代码,我们可以知道我们需要实现的函数有哪些,下面我们在源文件SeqList.c中实现这些函数,
1.顺序表的初始化
void SLInit(SL* ps)
{
ps->arr = NULL;//初始化时应该置为空,否则成为野指针
ps->size = ps->capacity = 0;
}
需要注意,凡是涉及到对顺序表的更改的,都必须要传地址过来 。
2.顺序表的尾插
void SLPushBack(SL* ps, SLDataType x)
{
assert(ps);
//判断ps是否为空
if (ps->size == ps->capacity)
//说明空间满了,需要申请空间
{
//判断空间是否为零
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* ret = (SLDataType*)realloc(ps->arr, sizeof(SLDataType) * newcapacity);
if (ret == NULL)
{
perror("realloc fail!");
exit(1);
}
ps->arr = ret;
ps->capacity = newcapacity;
}
ps->arr[ps->size++] = x;
}
在顺序表中插入数据时,我们需要判断顺序表的空间是否足够,如过不够,我们需要开辟一个更大的空间,而size表示有效数据个数,capacity表示顺序表空间大小,因此当有效数据个数等于顺序表空间大小即size=capacity时,表示空间已满,在开辟空间时,它的大小一般为原来空间大小的2倍,因此就出现了另一个情况,当size=capacity=0时,如果还是开辟原来空间大小的2倍的话,那不还是开辟了0个空间吗?因此,我们使用三目操作符来判断原来空间是否为0,如果为0则开辟4个顺序表数据类型的大小,如果不是0则开辟原来空间大小的2倍的空间。!!!当我们在插入数据后,顺序表的有效数据就多了一个,因此不要忘了size++
3.顺序表的头插
void SLPushFront(SL* ps, SLDataType x)
{
assert(ps);
if (ps->size == ps->capacity)
//说明空间满了,需要申请空间
{
//判断空间是否为零
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* ret = (SLDataType*)realloc(ps->arr, sizeof(SLDataType) * newcapacity);
if (ret == NULL)
{
perror("realloc fail!");
exit(1);
}
ps->arr = ret;
ps->capacity = newcapacity;
}
for (int i=ps->size;i>0;i--)
{
//让顺序表已有元素整体向后移动一位
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[0] = x;
ps->size++;
}
头插与尾插一样,首先要判断空间是否足够,头插具体方法如下图:
同样需要注意的是,在头插过后,顺序表的元素增加一个,size++
4.顺序表的尾删
void SLPopBack(SL* ps)
{
assert(ps);
//首先要判断顺序表是否为空,为空不能执行删除操作
assert(ps->size);
ps->size--;
}
顺序表的尾删非常简单,只需要让size前移一位即可,但是我们需要判断顺序表有效个数size是否为0,如果为零则无法进行尾删,并且头删过后,顺序白表有效元素个数减1,故size--
5.顺序表的头删
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->size);
for (int i = 0; i<ps->size-1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
顺序表头删思路如下图:
可见,我们只需将剩下的元素全部前移一位,把顺序表第一个元素覆盖掉,就完成了头删操作 ,在头删后,size也需要自减。
6.顺序表的打印
void SLPrint(SL s)
{
for (int i = 0; i < s.size; i++)
{
printf("%d ", s.arr[i]);
}
printf("\n");
}
7.顺序表的销毁
void SLDestroy(SL* ps)
{
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
用于顺序表的空间是动态申请的,故在使用完后要进行释放。
下面是SeqList.c的全部代码:
#include"Seqlist.h"
void SLInit(SL* ps)
{
ps->arr = NULL;//初始化时应该置为空,否则成为野指针
ps->size = ps->capacity = 0;
}
void SLJudgeCapacity(SL* ps)
{
if (ps->size == ps->capacity)
//说明空间满了,需要申请空间
{
//判断空间是否为零
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* ret = (SLDataType*)realloc(ps->arr, sizeof(SLDataType) * newcapacity);
if (ret == NULL)
{
perror("realloc fail!");
exit(1);
}
ps->arr = ret;
ps->capacity = newcapacity;
}
}
void SLDestroy(SL* ps)
{
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
void SLPushBack(SL* ps, SLDataType x)
{
assert(ps);
//判断ps是否为空
SLJudgeCapacity(ps);//头插和尾插都需要判断ps是否为空,因此可以将这段代码封装成一个函数
ps->arr[ps->size++] = x;
}
void SLPushFront(SL* ps, SLDataType x)
{
assert(ps);
if (ps->size == ps->capacity)
//说明空间满了,需要申请空间
{
//判断空间是否为零
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
SLDataType* ret = (SLDataType*)realloc(ps->arr, sizeof(SLDataType) * newcapacity);
if (ret == NULL)
{
perror("realloc fail!");
exit(1);
}
ps->arr = ret;
ps->capacity = newcapacity;
}
SLJudgeCapacity(ps);
for (int i=ps->size;i>0;i--)
{
//让顺序表已有元素整体向后移动一位
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[0] = x;
ps->size++;
}
void SLPopBack(SL* ps)
{
assert(ps);
//首先要判断顺序表是否为空,为空不能执行删除操作
assert(ps->size);
//ps->arr[ps->size - 1] = -1;
ps->size--;
}
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->size);
for (int i = 0; i<ps->size-1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
void SLPrint(SL s)
{
for (int i = 0; i < s.size; i++)
{
printf("%d ", s.arr[i]);
}
printf("\n");
}
8.代码测试
我们在test.c中进行代码测试,代码如下:
#include"Seqlist.h"
void SLTest01()
{
SL s1;
SLInit(&s1);//初始化
//增删查改
SLPushBack(&s1, 1);
SLPushBack(&s1, 2);
SLPushBack(&s1, 3);
SLPushBack(&s1, 4);
SLPushBack(&s1, 5);
SLPrint(s1);
SLPushFront(&s1, 9);
SLPrint(s1);
SLDestroy(&s1);//销毁
}
int main()
{
SLTest01();
return 0;
}
运行结果如下: