顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用顺序存储结构的线性表通常称为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。
typedef struct
{
int *elem; // 指向存储数据元素的空间的指针
int length; // 当前存储数据的个数
int listsize; // 当前分配的存储空间的大小
}SqList;
这个就是数据结构中顺序表的存储结构模式,其中data域为数据域,是指向存储数据元素的空间的指针;length为当前存储数据的个数,listsize为当前分配的存储空间的大小及链表的大小
1、顺序表的初始化
//初始化顺序表
void InitSqList(SqList *list)
{
assert(list != NULL);
list->elem = (int *)malloc(sizeof(int)* LEN);
list->length = 0;
list->listsize = LEN;
//printf("Init success\n");
}
2、顺序表的判满,扩充空间以及插入数据
插入函数
顺序表的插入有几种方法,头插,尾插以及任意位置插入,都是对顺序表进行插入操作。
// 在顺序表的指定位置pos插入数据val
int InsertSqList(SqList *list, int pos, int val)
{
assert(list != NULL);
if (pos < 0)
{
printf("error:pos is negative\n");
return 0;
}
if (IsFull(list))
{
ExpandSpace(list);
}
if (pos<=list->length)
{
for (int i = pos; i < list->length + 1; i++)
{
int tmp = list->elem[i];
list->elem[i] = val;
val = tmp;
}
}
else if (pos == list->length+1)
{
list->elem[pos] = val;
}
else
{
printf("error:pos is big\n");
return 0;
}
list->length++;
//printf("insert success\n");
return 1;
}
// 在顺序表的第一个位置插入数据
int InsertHead(SqList *list, int val)
{
assert(list != NULL);
if (IsFull(list))
{
ExpandSpace(list);
}
if (list->length >= 1)
{
for (int i = 0; i < list->length + 1; i++)
{
int tmp = list->elem[i];
list->elem[i] = val;
val = tmp;
}
}
else
{
list->elem[0] = val;
}
list->length++;
//printf("insert head place success\n");
return 1;
}
// 在顺序表的最后一个位置插入数据
int InsertTail(SqList *list, int val)
{
assert(list != NULL);
if (IsEmpty(list))
{
printf("error:form is empty\n");
return 0;
}
if (IsFull(list))
{
ExpandSpace(list);
}
list->elem[list->length] = val;
list->length++;
//printf("insert tail place success\n");
return 1;
}
由于顺序表的底层是由数组结构构成的,所以它有着初始的并且固定的大小,当我们进行插入过多的数据时,原有的数组大小不够存储,就需要进行对数组的扩充,即为顺序表的扩充。
//扩充空间
static int ExpandSpace(SqList *list)
{
assert(list != NULL);
int change = list->listsize * 2;
int * tmp = (int *)malloc(change * sizeof(int));
assert(tmp != NULL);
for (int i = 0; i < list->listsize; i++)
{
tmp[i] = list->elem[i];
}
list->listsize = change;
list->elem = (int *)malloc(list->listsize * sizeof(int));
assert(list->elem != NULL);
return 1;
}
既然数组我们要进行扩容,那么我们在什么时候扩容,这就需要对数组进行判满,如果顺序表已满,我们就可以进行扩容。
//判断是否为满
static int IsFull(SqList *list)
{
assert(list != NULL);
if (list->length == list->listsize)
{
return TRUE;
}
return FALSE;
}
3、顺序表的删除以及判空
既然有插入,那必定有删除函数,在删除函数之前,我们需要为顺序表进行判空,在顺序表不为空的情况下才可以进行删除数据。
//顺序表的判空操作
static int IsEmpty(SqList *list)
{
assert(list != NULL);
if (list->length == 0)
{
return TRUE;
}
return FALSE;
}
删除数据
当然删除数据也有头删,尾删,任意位置删除操作
// 删除指定位置的数据
int DeleteSqList(SqList *list, int pos)
{
assert(list != NULL);
if (pos < 0)
{
printf("error:pos is negative\n");
return 0;
}
if (IsEmpty(list) || pos >= list->length)
{
printf("error:form is empty\n");
return 0;
}
else
{
for (int i = pos; i < list->length; i++)
{
int tmp = list->elem[i];
list->elem[i] = list->elem[i+1];
list->elem[i + 1] = tmp;
}
}
list->length--;
//printf("delete success\n");
return 1;
}
// 删除第一个数据元素
int DeleteHead(SqList *list)
{
assert(list != NULL);
if (IsEmpty(list))
{
printf("error:form is empty\n");
return 0;
}
if (list->length > 1)
{
for (int i = 0; i < list->length; i++)
{
int tmp = list->elem[i];
list->elem[i] = list->elem[i + 1];
list->elem[i + 1] = tmp;
}
}
else
{
list->elem[0] = NULL;
}
list->length--;
//printf("delete head place success\n");
return 1;
}
// 删除最后一个数据元素
int DeleteTail(SqList *list)
{
assert(list != NULL);
if (IsEmpty(list))
{
printf("error:form is empty\n");
return 0;
}
list->elem[list->length - 1] = NULL;
list->length--;
//printf("delete tail place success\n");
return 1;
}
4、顺序表中数据的查找
// 查找val所在的位置(最后一次出现的位置)
int FindVal(SqList *list, int val)
{
assert(list != NULL);
int index = 0;
if (IsEmpty(list))
{
printf("error:form is empty\n");
return 0;
}
for (int i = 0; i < list->length - 1; i++)
{
if (list->elem[i] == val)
{
index = i;
}
else
{
printf("no found\n");
return 0;
}
}
printf("index=%d\n",index);
return index;
}
5、顺序表的销毁
//销毁顺序表
void DestroySqList(SqList *list)
{
assert(list != NULL);
free(list->elem);
list->length = list->listsize = 0;
//printf("Destroy success\n");
}
这次我都是用C语言进行实现,C++也是差不多,就不再多阐述了。
6、顺序表的使用场景
缺点:
插入删除并不方便:由于顺序表的底层实现是由数组实现的,数组中的数据都是按顺序存储的,并且还需要进行对数组的扩容,这就导致了当我们在插入数据时除了尾插,头插和任意位置插入数据都需要对数组进行很大幅度的调整,删除时也是一样,除了尾删,头删和任意位置删除都是需要对数组的结果进行调整的,时间复杂度较高。
优点:
查找方便:由于是数组构成的,所以在查找是只需对下标进行查找,所以时间复杂度较低,查找的效率很高。
所以顺序表一般使用在对数据的插入和删除操作不频繁,插入之后基本不会再会对顺序表中存储的数据进行变化的场景,多半利用在经常进行查找操作的情况下。