一.线性表的定义
线性表的顺序表示又称为顺序存储结构或顺序映像。
1.顺序表简介:
百度百科:顺序表是在计算机内存中以数组的形式保存的线性表,线性表的n是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系,采用顺序存储结构的线性表通常称为顺序表。顺序表是将表中的结点依次存放在计算机内存中一组地址连续的存储单元中。
概念: 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序存储定义:逻辑上相邻的数据元素存储在物理上相邻的存储单元中的存储结构。
总之:逻辑上相邻,物理上也相邻。
线性表(A1,A2,A3.....An-1,An)
线性表的第一个数据元素A1的存储位置,称作线性表的起始位置或基地址。
存储结构:依次存储,地址连续,中间没有空出存储单元
顺序表的特点:以物理位置相邻表示逻辑关系。任意元素可随机存取
顺序表元素:地址连续、依次存放、随机存取、类型相同
2.定义总结:
二.顺序表分类
分为静态顺序表与动态顺序表(如上图所示)
静态顺序表:使用定长数组存储元素。
动态顺序表:使用动态开辟的数组存储。
优缺点:
1.随机访问
2.存储密度高
3.扩展容量不方便
4.插入、删除数据元素不方便
三.代码实现
3.1 【1】定义顺序表的最大长度和动态顺序表的初始默认最大容量
/*需要包含的头文件*/
#include <stdlib.h>
#include <stdio.h>
/*【1】定义顺序表的最大长度和动态顺序表的初始默认最大容量*/
#define MaxSize 10
#define InitSize 10
/*程序演示开头展示*/
char welcome[] = "~~~~~~~~~~~欢迎光临本程序~~~~~~~~~如有问题~~~~~~~请选择帮助联系开发人员\n顺序表初始化中......";
3.2 【2】自定义C语言的bool变量
/*【2】自定义C语言的bool变量*/
#define bool char
#define true 1
#define false 0
3.3 【3】自定义数据元素的数据类型
/*【3】自定义数据元素的数据类型*/
typedef int Elemtype;
3.4 【4】顺序表的结构体定义
/*【4】顺序表的结构体定义*/
//<1>静态顺序表的数据元素结构体
typedef struct SqList
{
/*使用静态数组存放数据元素 */
Elemtype data[MaxSize];
/*顺序表的当前长度*/
int length;
}SqList;
//<2>动态顺序表的数据元素结构体
typedef struct SeqList
{
/*使用动态指针分配数组 */
Elemtype *data;
/*顺序表的当前长度*/
int length;
/*顺序表的当前长度*/
int maxsize;
}SeqList;
3.5 【5】顺序表的初始化(这里初始化长度均为5,方便后面代码测试)
/*【5】顺序表的初始化*/
//<1>静态顺序表初始化
bool InitSqList(SqList *L){
//初始化表长
L->length = 5;
return true;
}
//<2>动态顺序表初始化
bool InitSeqList(SeqList *L){
//初始化表长
L->length = 5;
//初始化最大容量
L->maxsize = InitSize;
//给指针分配一片空间,空间大小依据maxsize决定
L->data = (Elemtype *)malloc(sizeof(Elemtype) * InitSize);
return true;
}
3.6 【6】顺序表的元素插入
/*【6】顺序表的元素插入*/
//<1>静态顺序表的插入
bool SqListInsert(SqList *L, int i, Elemtype e){ //在顺序表的第i个位置插入新元素e
//[1]判断插入操作是否合法
if (i<1 || i>L->length+1)
{
printf("【10001】这个位置插入不合法!\n");
return false;
}
if (L->length >= MaxSize)
{
printf("【10002】这张表已经满了!\n");
return false;
}
//[2]将第i位和之后的元素位置往后移动一位
for (int j = L->length; j >= i; j--)
{
L->data[j] = L->data[j-1];
}
//[3]将新元素插入到指定位置---区分数组下标和元素位序的排列
L->data[i - 1] = e;
//[4]表长加一
L->length++;
return true;
}
//<2>动态顺序表的插入
bool SeqListInsert(SeqList *L, int i, Elemtype e){
//[1]判断插入操作是否合法
if (i<1 || i>L->length+1)
{
printf("【10003】这个位置插入不合法!\n");
return false;
}
if(L->length >= L->maxsize){
printf("【10004】这张表已经满了!\n");
return false;
}
//[2]将第i位和之后的元素位置往后移动一位
for (int j = L->length; j >= i; j--)
{
L->data[j] = L->data[j-1];
}
//[3]将新元素插入到指定位置
L->data[i-1] = e;
//[4]表长加一
L->length++;
return true;
}
3.7 【7】顺序表的元素删除
/*【7】顺序表的元素删除*/
//<1>静态顺序表的删除
//将第i位元素删除
bool SqListDetele(SqList *L,int i,Elemtype *e){
//[1]判断操作合法性
if (i<1 || i>L->length+1)
{
printf("【10005】这个位置删除不合法!\n");
return false;
}
if (L->length <= 0)
{
printf("【10006】这张表是空的!\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--;
//[5]返回TRUE
return true;
}
//<2>动态顺序表的删除
bool SeqListDetele(SeqList *L,int i,Elemtype *e){
//[1]判断操作合法性
if (i<1 || i>L->length+1)
{
printf("【10005】这个位置删除不合法!\n");
return false;
}
if (L->length <= 0)
{
printf("【10006】这张表是空的!\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--;
//[5]返回TRUE
return true;
}
3.8 【8】顺序表的查询(按值查询)
/*【8】顺序表的查询(按值查询)*/
//<1>静态顺序表的查询
int SqListSelect(SqList L,Elemtype e){
for (int i = 0; i < L.length; i++)
{
if (L.data[i] == e)
{
return i+1;
}
}
return 0;
}
//<2>动态顺序表的查询
int SeqListSelect(SeqList L,Elemtype e){
for (int i = 0; i < L.length; i++)
{
if (L.data[i] == e)
{
return i+1;
}
}
return 0;
}
3.9 【9】动态顺序表的扩容
/*【9】动态顺序表的扩容*/
bool IncreaseSize(SeqList *L,int len){
//[1]生成指向原来顺序表存储空间的指针
Elemtype *p = L->data;
//[2]开辟空间
L->data = (Elemtype *)malloc(sizeof(Elemtype) * (L->maxsize + len));
//[3]转移数据
for (int i = 0; i < L->length; i++)
{
L->data[i] = p[i];
}
//[4]修改顺序表长,值加len
L->maxsize += len;
//[5]释放空间
free(p);
return true;
}
3.10 【10】顺序表的输出
/*静态顺序表的输出*/
bool SqListPrint(SqList L){
//判空
if (L.length == 0)
{
printf("This sequence table is emply!(空表)\n");
}else{
printf("SqList===");
}
for (int i = 0; i < L.length; i++)
{
printf("%d、",L.data[i]);
}
printf("\n");
printf("end\n");
}
/*动态顺序表的输出*/
bool SeqListPrint(SeqList L){
//判空
if (L.length == 0)
{
printf("This sequence table is emply!(空表)\n");
}else{
printf("SeqList===");
}
for (int i = 0; i < L.length; i++)
{
printf("%d、",L.data[i]);
}
printf("\n");
printf("end\n");
}
3.11 【11】main方法
int main(){
SqList L1;
SeqList L2;
int a,b,d,e,num,num1,choose;
for(int i=0;i<strlen(welcome);i++)
{
printf("%c",welcome[i]);
for(int m=0;m<10000;m++)
for(int n=0;n<2800;n++)
{
}
}
//对顺序表初始化
if (InitSqList(&L1) == true && InitSeqList(&L2) == true)
{
printf("顺序表初始化成功!\n");
}else{
printf("初始化失败啦!快去找bug!\n");
}
while (choose != 9 )
{
printf("请选择需要调试的顺序表:\n");
printf("静态顺序表:【1】\n");
printf("动态顺序表:【2】\n");
printf("更多:【3】\n");
printf("帮助:【4】\n");
printf("关闭:【9】\n");
scanf("%d",&choose);
//静态顺序表
if (choose == 1)
{
while (num1 != 6)
{
printf("---------------------------------\n");
printf("打印静态顺序表【0】\n");
printf("设置静态顺序表的数据元素【1】\n");
printf("静态顺序表中插入数据元素【2】\n");
printf("静态顺序表中删除数据元素【3】\n");
printf("重新初始化静态顺序表【4】\n");
printf("按值查找元素位序【5】\n");
printf("退出【6】\n");
printf("---------------------------------\n");
printf("\n");
scanf("%d",&num1);
switch (num1)
{
//选项0
case 0:
printf("静态顺序表打印结果===》\n");
SqListPrint(L1);
break;
//选项1
case 1:
for (int i = 1; i <= L1.length; i++)
{
printf("请输入第%d个数字:",i);
scanf("%d",&a);
L1.data[i-1] = a;
}
printf("设置成功!\n");
SqListPrint(L1);
break;
//选项2
case 2:
//静态顺序表插入元素
printf("请输入想要插入的位置====");
scanf("%d",&a);
printf("请输入想要插入的数字====");
scanf("%d",&b);
bool l = SqListInsert(&L1,a,b);
if (l == false)
{
printf("插入失败!\n");
}else{
printf("插入成功!\n");
}
printf("静态顺序表打印结果===》\n");
SqListPrint(L1);
break;
//选项3
case 3:
printf("请输入想要删除的位置====");
scanf("%d",&a);
SqListDetele(&L1,a,&e);
printf("删除成功!已将%d删除\n",e);
printf("重新打印静态顺序表:\n");
SqListPrint(L1);
break;
//选项4
case 4:
//对顺序表初始化
printf("静态顺序表初始化中......\n");
if (InitSqList(&L1) == true)
{
printf("静态顺序表初始化成功!\n");
}else{
printf("初始化失败啦!快去找bug!\n");
}
break;
//选项5
case 5:
printf("请输入想要查找的元素====");
scanf("%d",&d);
printf("该值在第%d位第一次出现\n",SqListSelect(L1,d));
break;
//选项6
// case 6:
// break;
}
}
}
//动态顺序表
else if(choose == 2)
{
while (num1 != 7){
printf("---------------------------------\n");
printf("打印动态顺序表【0】\n");
printf("设置动态顺序表的数据元素【1】\n");
printf("动态顺序表中插入数据元素【2】\n");
printf("动态顺序表中删除数据元素【3】\n");
printf("重新初始化动态顺序表【4】\n");
printf("按值查找元素位序【5】\n");
printf("动态顺序表扩容【6】\n");
printf("退出【7】\n");
printf("---------------------------------\n");
printf("\n");
scanf("%d",&num1);
switch (num1)
{
//选项0
case 0:
printf("动态顺序表打印结果===》\n");
SeqListPrint(L2);
break;
//选项1
case 1:
for (int i = 1; i <= L2.length; i++)
{
printf("请输入第%d个数字:",i);
scanf("%d",&a);
L2.data[i-1] = a;
}
printf("设置成功!\n");
SeqListPrint(L2);
break;
//选项2
case 2:
//动态顺序表插入元素
printf("请输入想要插入的位置====");
scanf("%d",&a);
printf("请输入想要插入的数字====");
scanf("%d",&b);
bool l = SeqListInsert(&L2,a,b);
if (l == false)
{
printf("插入失败!\n");
}else{
printf("插入成功!\n");
}
printf("动态顺序表打印结果===》\n");
SeqListPrint(L2);
break;
//选项3
case 3:
printf("请输入想要删除的位置====");
scanf("%d",&a);
SeqListDetele(&L2,a,&e);
printf("删除成功!已将%d删除\n",e);
printf("重新打印动态顺序表:\n");
SeqListPrint(L2);
break;
//选项4
case 4:
//对顺序表初始化
printf("动态顺序表初始化中......\n");
if (InitSeqList(&L2) == true)
{
printf("动态顺序表初始化成功!\n");
}else{
printf("初始化失败啦!快去找bug!\n");
}
break;
//选项5
case 5:
printf("请输入想要查找的元素====");
scanf("%d",&d);
printf("该值在第%d位第一次出现\n",SeqListSelect(L2,d));
break;
//选项6
case 6:
printf("空间大小:%d\n",L2.maxsize);
printf("想要扩容的大小=====");
scanf("%d",&d);
IncreaseSize(&L2,d);
printf("新的空间大小:%d\n",L2.maxsize);
break;
//选项7
//case 7:
//break;
}
}
}else if (choose == 3)
{
printf("还没开发\n");
}else if (choose == 4)
{
printf("~~~~~本程序由小露制作,用于了解顺序表的原理,并且示范两种顺序表的基本操作\n如有问题也没办法,程序员已经跑路了~~~~~\n");
}
else if (choose == 9)
{
break;
}
else{
printf("输入不合法!请重新输入!\n");
}
}
}
四.运行结果
1.静态顺序表:
3.动态顺序表:
五.小结
顺序表是一种常见的线性表数据结构,它通过数组实现,有固定的大小并且元素在内存中是连续存储的。下面是顺序表的小结:
特点:顺序表的元素在内存中是连续存储的,可以通过索引直接访问元素,具有随机访问的特性。它的大小是固定的,一旦定义了大小,就无法动态调整。
缺点:顺序表的大小固定,一旦初始化就无法改变,当元素数量超过顺序表大小时,需要重新申请更大的内存空间,并将元素复制到新的空间中。这个过程比较耗时。
插入和删除:在顺序表中插入和删除元素需要移动元素的位置,这可能涉及到大量的数据搬移操作,特别是在顺序表的中间位置插入或删除元素时,效率较低。
查找:在顺序表中查找元素的时间复杂度为O(n),需要遍历整个顺序表,直到找到匹配的元素。如果需要经常进行查找操作,顺序表可能不是最佳选择。
动态扩展:由于顺序表的大小固定,当元素数量增加时,可能需要重新申请更大的内存空间。这个过程可能比较耗时,需要考虑内存分配和数据迁移的开销。
总之,顺序表是一种简单且常用的数据结构,适用于对元素随机访问频繁、元素数量固定且不需要频繁插入和删除的场景。但对于元素数量动态变化、插入和删除较频繁的情况,顺序表的性能可能不如链表等动态分配内存的数据结构。选择使用何种数据结构需要根据具体的需求和场景来进行权衡。
参考文献:
百度百科,CSDN,数据结构(C语言版)