1.线性表
![](https://i-blog.csdnimg.cn/blog_migrate/8dc1a7f44298639adb46aa6388ce23a5.png)
反例:二叉树
2.顺序表
2.1顺序表的定义
1.静态顺序表:使用定长数组存储元素。
//静态顺序表的结构
typedef struct SeqList
{
Datatype a[N];
int size;
}Static_SeqList;
//其中Datatype为存储顺序表的数据类型
//N为人为定义的数组最大个数
//size为顺序表中元素的个数
2.动态顺序表:使用动态开辟的数组存储。
//动态顺序表的结构
typedef struct SeqList
{
Datatype *array;
int size;
int capacity;
}Dynamic_SeqList;
//其中Datatype为存储顺序表的数据类型
//array为动态顺序表的首地址
//size为顺序表中元素的个数
//capacity为动态顺序表的最大容量
而在使用中,我们一般会使用动态顺序表更为方便,故只考虑动态顺序表的接口。
2.2动态顺序表的基本操作
2.2.1动态顺序表的创建
对于动态顺序表的创建,我们需要进行以下操作
1.以函数传来的地址为首地址开辟一块新的空间
2.检测空间是否开辟成功
3.初始化顺序表结构中各个元素的量
void SeqListInit(SeqList* ps)
{
ps->a = (SLDateType*)malloc(sizeof(SLDateType) * 4);
//开辟了4个空间
if (ps->a == NULL)
{
perror("malloc error");
return;
}
//检测空间是否开辟成功
ps->capacity = 4;
//该顺序表最大容量是开辟的4个空间
ps->size = 0;
//该顺序表没有元素
}
2.2.2动态顺序表的删除
对于动态顺序表的删除,我们需要进行以下操作
1.清空动态顺序表中的元素
2.将动态顺序表的最大容量和元素个数归零
void SeqListDestroy(SeqList* ps)
{
free(ps->a);
//清空元素
ps->capacity = 0;
ps->size = 0;
//归零
}
2.2.3动态顺序表的打印
对于动态顺序表的打印,我们只需遍历动态顺序表,然后打印出遍历的每一个元素
void SeqListPrint(SeqList* ps)
{
for (int i = 0; i < ps->size; i++)//遍历动态顺序表
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
2.2.4动态顺序表的尾插
对于动态顺序表的尾插,我们需要进行以下操作
1.检查动态顺序表元素是否达到上限(若达到上限则需要扩容)
2.在尾部插入元素
void SeqListPushBack(SeqList* ps, SLDateType x)
{
if (ps->size == ps->capacity)//检查元素是否达到上限
{
SLDateType *tmp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * ps->capacity*2);
//扩容,将数组的容量扩容到原来的两倍
if (tmp == NULL)
{
perror("realloc fail");
return;
}
//检查是否扩容成功
ps->a = tmp;
ps->capacity *= 2;
}
ps->a[ps->size] = x;
ps->size++;
}
此时便可以体现出顺序表的优点之一:可以随机访问任一元素而不需遍历整个数组
2.2.5动态顺序表的头插
对于动态顺序表的尾插,我们需要进行以下操作
1.检查动态顺序表元素是否达到上限(若达到上限则需要扩容)
2.将顺序表中所有元素从后往前开始向后挪动一个单位的地址
(注:一定必须从后往前开始挪动,如果从前往后开始挪动,则在后一个元素还没有挪动之前,前一个元素会对其进行覆盖)
3.在首地址插入新元素
void SeqListPushFront(SeqList* ps, SLDateType x)
{
if (ps->size == ps->capacity)
{
SLDateType* tmp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * ps->capacity*2);
if (tmp == NULL)
{
perror("realloc error");
return;
}
ps->a = tmp;
ps->capacity *= 2;
}
//检查扩容
for (int i = ps->size ; i > 0; i--)
{
ps->a[i] = ps->a[i - 1];
}//从后往前开始挪动数组
ps->a[0] = x;
ps->size++;
}
2.2.6动态顺序表的尾删
对于动态顺序表的尾删,我们不需要将尾部的数据改为0,因为数组size后面的元素我们不需要关心他们的具体值,我们无法访问到也不会去访问那些没有被定义的具体值
但是对于任意一个数据结构的节点删除,我们都必须判断其是否为空,若为空进行删除,很可能会导致越界的问题
void SeqListPopBack(SeqList* ps)
{
assert(ps->size > 0);
//判断数组是否为空
ps->size--;
}
2.2.7动态顺序表的头删
对于动态顺序表的头删,和尾删一样,我们不需要关心删除后size外的其它元素,只需要从前往后开始对其进行依次覆盖
void SeqListPopFront(SeqList* ps)
{
assert(ps->size > 0);
for (int i = 0; i < ps->size; i++)
{
ps->a[i] = ps->a[i + 1];
}//从前往后开始依次进行覆盖
ps->size--;
}
2.2.8动态顺序表的查找
对于动态顺序表的查找,我们只需要遍历整个动态顺序表,若找到元素则返回元素的位置,若没有找到元素则返回-1
int SeqListFind(SeqList* ps, SLDateType x)
{
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i+1;
}
}
return -1;
}
2.2.9动态顺序表在任一位置的插入
其原理和动态顺序表头插和尾插相同,只需要在该位置往后所有元素向后挪动一个单位,然后在该单位插入新元素即可
头插和尾插是该操作的一种特殊情况
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDateType x)
{
assert(pos <= ps->size);
if (ps->size == ps->capacity)
{
SLDateType* tmp = (SLDateType*)realloc(ps->a, sizeof(SLDateType) * ps->capacity);
if (tmp == NULL)
{
perror("realloc fail");
return;
}
ps->a = tmp;
ps->capacity *= 2;
}
for (int i = ps->size; i > pos-1; i--)
{
ps->a[i] = ps->a[i - 1];
}
ps->a[pos - 1] = x;
ps->size++;
}
2.2.10动态顺序表在任一位置的删除
其原理和动态顺序表头删和尾删相同,只需在该位置往后所有元素向前挪动一个单位即可
// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos)
{
assert(ps->size);
assert(pos <= ps->size);
for (int i = pos - 1; i < ps->size; i++)
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}