引言
在计算机科学中,数据结构是组织和存储数据的方式,它影响着数据的访问和修改效率。顺序表作为最简单且直观的数据结构之一,以连续内存空间存储数据,为数据的快速访问提供了便利。本文将深入探讨顺序表的实现机制,包括其基本原理、关键操作及应用场景,旨在为初学者和进阶学习者提供一份全面的指南。
一.顺序表的定义及其特点
定义
顺序表是一种基本的数据结构,它通过连续的内存空间来存储数据元素,每个元素占据相同的存储空间,且在逻辑上相邻的元素在物理位置上同样相邻(存储结构)。
特点
1. 随机访问,访问速度快,顺序表在随机访问方面具有O(1)的时间复杂度。
2.存储密度高:顺序表在内存中是连续存储的,它不需要额外的空间来存储指针或其他控制信息。
3.扩展容量不方便:顺序表中的元素数量达到其容量极限时,为了插入新的元素,需要进行内存的重新分配
4.插入和删除元素不方便:插入和删除操作可能需要O(n)的时间复杂度,通常可能涉及大量元素的移动。
二.顺序表的实现 (静态分配和动态分配)
准备工作
头文件
#include<stdlib.h>
#include<stdio.h>
[1] 定义静态顺序表的最大长度和动态顺序表的初始最大容量
#define Maxsize 10 //静态顺序表的最大长度
#define InitSize 10 //动态顺序表的默认最大初始容量
[2] 自定义c语言的bool变量
#define true 1
#define false 0
[3] 自定义数据元素的类型
typedef int Elemtype;
结构体
我们使用俩种方法实现顺序表。其中data为数据的类型,length为顺序表数当前的长度,动态分配比静态分配多一个参数 maxsize,用于记录顺序表的最大容量。
代码:
// <1>静态分配
typedef struct SqList {
Elemtype data[Maxsize];//用静态数组存放数据元素
int length; //顺序表当前长度
}SqList;
// <2>动态分配
typedef struct SeqList
{
Elemtype* data;//用动态指针 分配数组
int length;//当前长度
int maxsize;//最大容量
}SeqList;
顺序表的初始化
注意:我们在主函数中引用定义一个顺序表再引用该函数,并将顺序表的指针传入函数。
对于插入,删除等操作 参数为指针 (SqList *L),这样在方法中对顺序表的修改效果才能实际生效。
对于查找顺序表的元素、和输出顺序表的值,他们的参数不为指针(为 SqList L),因为我们不需要对表进行改变。
// <1>顺序表的初始化(静态分配)
bool InitSqList(SqList* L)//参数为指针,要对表内做出改变
{
L->length = 0;//初始化表长
//for (int i = 0; i < Maxsize; i++) //初始化元素
//{
// L->data[i] =0;
//}
return true;
}
// <2>顺序表的初始化(动态分配)
bool InitSeqList(SeqList* L)
{
L->length = 0;//初始化表长
L->maxsize = InitSize;//初始化动态顺序表的最大容量
L->data = (Elemtype*)malloc(sizeof(Elemtype) * InitSize);//malloc
return true;
}
顺序表的输出
//<1>静态顺序表的输出
bool SqListPrint(SqList L)
{
//<1>判断是否为空
if (L.length == 0)
{
printf("This sequence table is empty! \n");
return false;
}
//<2>输出
printf("SqList: \n");
for (int i = 0; i < L.length; i++)
{
printf("%d-->",L.data[i]);
}
printf("end\n");
return true;
}
//<2>动态顺序表的输出
bool SeqListPrint(SeqList L)
{
//<1>判断是否为空
if (L.length == 0)
{
printf("This sequence table is empty! \n");
return false;
}
//<2>输出
printf("SeqList: \n");
for (int i = 0; i < L.length; i++)
{
printf("%d-->", L.data[i]);
}
printf("end\n");
return true;
}
插入元素实现
插入操作涉及以下几个步骤
1.检查插入操作的合法性:插入位置是否合理?插入的表元素是否满了?
2.移动元素以腾出空间:从要插入的位置i开始,将顺序表中的元素向后移动,为新元素腾出空间。这通常通过一个从后向前的循环来实现。
3.插入新元素:在腾出的空间中放置新元素。
4.更新顺序表的长度:插入新元素后,顺序表的长度需要增加1。
//<1>顺序表的元素插入(静态分配)
//在顺序表的第i个位置插入一个新元素e
bool SqListInsert(SqList* L ,int i, Elemtype e)
{
//[1]判断插入操作是否合法
if (i<1 || i>L->length + 1)//length 为当前长度
{
printf("The position of the element to be inserted is invalid \n");
return false;
}
if (L->length >= Maxsize)
{
printf("This sequence table is full! \n");
return false;
}
//[2]移动元素,空出待插入的位置
for (int j = L->length; j >= i; j--)
{
L->data[j] = L->data[j - 1];
}
//[3]将新元素插入到正确的位置 -----区分数组小标和元素位序的排列
L->data[i - 1] = e;
//[4]表长加一
L->length++;
//[5]返回,插入成功
return true;
}
// <2>顺序表的插入 (动态分配)
bool SeqListInsert(SeqList* L, int i, Elemtype e)
{
//[1]判断插入操作是否合法
if (i<1 || i>L->length + 1)//length 为当前长度
{
printf("The position of the element to be inserted is invalid \n");
return false;
}
if (L->length >= L->maxsize)
{
printf("This sequence table is full! \n");
return false;
}
//[2]移动元素,空出待插入的位置
for (int j = L->length; j >= i; j--)
{
L->data[j] = L->data[j - 1];
}
//[3]将新元素插入到正确的位置 -----区分数组小标和元素位序的排列
L->data[i - 1] = e;
//[4]表长加一
L->length++;
//[5]返回,插入成功
return true;
}
删除元素实现(按位删除)
//<1>静态顺序表的元素删除
//将顺序表的第i个位置的元素删除,并把删除元素的值返回给e
bool SqListElemDelete(SqList* L, int i, Elemtype *e)
{
//[1]判断操作的合法性
if (i<1 || i>L->length + 1)//length 为当前长度
{
printf("The position of the element to be delete is invalid \n");
return false;
}
if (L->length <= 0)
{
printf("This sequence table is empty! \n");
return false;
}
//[2]将待删除的元素赋值给e
*e = L->data[i - 1];
//[3]将第i位以及后面的元素都往前移一位
for (int j = i; j < L->length; j++)
{
L->data[j - 1] = L->data[j];
}
//[4]表长减一
L->length--;
//返回
return true;
}
//<2>动态顺序表的元素删除
//将顺序表的第i个位置的元素删除,并把删除元素的值返回给e
bool SeqListElemDelete(SeqList* L, int i, Elemtype* e)
{
//[1]判断操作的合法性
if (i<1 || i>L->length + 1)//length 为当前长度
{
printf("The position of the element to be delete is invalid \n");
return false;
}
if (L->length <= 0)
{
printf("This sequence table is empty! \n");
return false;
}
//[2]将待删除的元素赋值给e
*e = L->data[i - 1];
//[3]将第i位以及后面的元素都往前移一位
for (int j = i; j < L->length; j++)
{
L->data[j - 1] = L->data[j];
}
//[4]表长减一
L->length--;
//返回
return true;
}
查找元素实现(按值查找)
//<1>静态顺序表的元素按值查找,并返回位序
int SqListLocElem(SqList L, Elemtype e)
{
for (int i = 0; i < L.length; i++)
{
if (L.data [i] == e)
{
return i + 1;
}
}
return 0;
}
//<2>动态顺序表的元素按值查找,并返回位序
int SeqListLocElem(SeqList L, Elemtype e)
{
for (int i = 0; i < L.length; i++)
{
if (L.data[i] == e)
{
return i + 1;
}
}
return 0;
}
扩容
/*【9】动态顺序表 --- 扩容*/
bool IncreaseSize(SeqList* L, int len)
{
//[1]生成指向原来顺序表的存储空间的指针
Elemtype* p = L->data;
//[2]为顺序表开辟一块更大的空间
L->data = (Elemtype*)malloc(sizeof(Elemtype) * (Maxsize + len));
//[3]转移数据
for (int i = 0; i < L->length; i++)
{
L->data[i] = p[i]; //p[i] == = *(p +i)
}
//修改顺序表的最大长度
L->maxsize += len;
//释放原来的空间
free(p);
//成功返回
return true;
}
测试
int main()
{
SqList L1; //静态顺序表L1
SeqList L2; //动态顺序表 L2
//顺序表的初始化
InitSqList(&L1);
InitSeqList(&L2);
printf("%d", L2.maxsize);
//插入一些元素
SqListInsert(&L1, 1, 1);
SqListInsert(&L1, 2, 2);
SqListInsert(&L1, 3, 3);
SqListInsert(&L1, 4, 4333);
SqListInsert(&L1, 5, 5);
SqListInsert(&L1, 6, 6);
SqListInsert(&L1, 7, 7);
SeqListInsert(&L2, 1, 1);
SeqListInsert(&L2, 2, 2);
SeqListInsert(&L2, 3, 3);
SeqListInsert(&L2, 4, 4);
SeqListInsert(&L2, 5, 5);
SeqListInsert(&L2, 6, 6999);
SeqListInsert(&L2, 7, 7);
SqListPrint(L1);
SeqListPrint(L2);
printf("L1 's 4333 is: %d\n", SqListLocElem(L1, 4333));
printf("L2 's 6999 is: %d\n", SeqListLocElem(L2, 6999));
int e;
SqListElemDelete(&L1, 4, &e);
printf("%d\n", e);
SeqListElemDelete(&L2, 6, &e);
printf("%d\n", e);
SqListPrint(L1);
SeqListPrint(L2);
printf("L1 's 4333 is: %d\n", SqListLocElem(L1, 4333));
printf("L2 's 6999 is: %d\n", SeqListLocElem(L2, 6999));
IncreaseSize(&L2, 20);
SeqListPrint(L2);
printf("L2 Maxsize: ---- %d", L2.maxsize);
return 0;
}