顺序表的定义
- 用一片连续的存储空间存放线性表,也就是逻辑上相邻的元素,在物理地址上也是相邻的
- 假设线性表L存储的起始位置为 L O C ( A ) LOC(A) LOC(A),每个数据元素占用的内存空间为 s i z e o f ( E l e m T y p e ) \rm sizeof(ElemType) sizeof(ElemType),则存储结构图为
数组下标 | 顺序表 | 内存地址 |
---|---|---|
0 | a 1 a_1 a1 | L O C ( A ) LOC(A) LOC(A) |
1 | a 2 a_2 a2 | L O C ( A ) + s i z e o f ( E l e m T y p e ) LOC(A)+\rm sizeof(ElemType) LOC(A)+sizeof(ElemType) |
⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ |
i − 1 i-1 i−1 | a i a_i ai | L O C ( A ) + ( i − 1 ) s i z e o f ( E l e m T y p e ) LOC(A)+\rm (i-1)sizeof(ElemType) LOC(A)+(i−1)sizeof(ElemType) |
⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ |
n − 1 n-1 n−1 | a n a_n an | L O C ( A ) + ( n − 1 ) s i z e o f ( E l e m T y p e ) LOC(A)+\rm (n-1)sizeof(ElemType) LOC(A)+(n−1)sizeof(ElemType) |
⋯ \cdots ⋯ | ⋯ \cdots ⋯ | ⋯ \cdots ⋯ |
M a x S i z e − 1 MaxSize-1 MaxSize−1 | a M a x S i z e a_{MaxSize} aMaxSize | L O C ( A ) + ( M a x S i z e − 1 ) s i z e o f ( E l e m T y p e ) LOC(A)+\rm (MaxSize-1)sizeof(ElemType) LOC(A)+(MaxSize−1)sizeof(ElemType) |
顺序表的基本操作
- 顺序表在定义数据结构时,有两种方式:
分配内存方式 | 做法 | 优点 | 缺点 |
---|---|---|---|
静态分配 | 使用数组,最开始就定义一个大小固定的数组 | 运行速度快 | 如果最开始分配的内存太小,则可能不满足问题需求,如果分配内存太大,又可能用不完,造成内存浪费 |
动态分配 | 定义一个指向目标数据类型的指针,在运行中动态申请内存1 | 需要多少内存,就申请多少内存,最大限度的使内存空间满足需求,并且不造成浪费 | 运行速度可能会慢(如果存储空间不够,将存储空间进行扩展2 时候会消耗时间)一点 |
静态顺序表的相关操作
- 数据结构定义
#define MAX_SIZE 20 //静态顺序表的最大长度
typedef int ElementType;
//静态顺序表
typedef struct Node{
ElementType Data[MAX_SIZE]; //静态顺序表使用静态数组,长度为最大长度
int length; //数据用来记录目前存入的数据长度
}SeqList;
typedef SeqList* List1;
- 顺序表初始化
- 使用一维数组对顺序表进行初始化
- 需要检查顺序表存储空间是否足够存下数组中的数据
//静态顺序表相关操作
//1.数据初始化
void Init1(List1 L,ElementType *Data,int Data_Size)
{
L->length = 0;
//检查顺序表空间是否足够
if(Data_Size > MAX_SIZE)
{
printf("初始化失败,数据过多,请重试\n");
return;
}
for(int i = 0;i < Data_Size;i++) //循环语句进行初始化线性表操作
{
L->Data[i] = Data[i];
L->length++; //这一句放到最后,写一条 L->length = Data_Size 亦可
}
}
- 按位置找到对应的值
- 要判断给的位置是否符合顺序表的范围
//2.按位置找到对应的值
ElementType Find_Ac_Position(List1 L,int Position)
{
if(Position < 1 || Position > L->length) //位置不在顺序表中,直接退出
{
printf("输入位置有误,请重新输入\n");
return -1;
}
return L->Data[Position - 1];
}
- 按值查找位置
- 在这里涉及到了查找问题,没有很细节的考虑高效的算法,具体的查找算法在查找章节中具体探究
//3.按值查找位置
int Find_Position(List1 L,ElementType value)
{
for(int i = 0;i < L->length;i++)
if(L->Data[i] == value)
return i + 1;
return -1;
}
- 插入数据
- 检查位置是否合法
- 检查顺序表是否已满
//4.插入一个数据
void Insert1(List1 L,ElementType value,int Position)
{
//检查插入位置的合法性
if(Position < 1 || Position > L->length + 1)
{
printf("插入位置不合法\n");
return;
}
//检查顺序表是否已满
if(L->length == MAX_SIZE)
{
printf("顺序表已满,不可以插入\n");
return;
}
//插入元素
for(int i = L->length - 1;i >= Position - 1;i--)
L->Data[i + 1] = L->Data[i];
L->Data[Position - 1] = value;
L->length++;
}
- 删除数据
- 检查删除元素的位置是否合法
- 判断顺序表是否已经是空表
//5.删除一个数据
void Delete1(List1 L,int Position)
{
//检查删除元素位置的合法性
if(Position < 1 || Position > L->length)
{
printf("删除元素的位置有误,删除失败\n");
return;
}
//检查顺序表是否已经为空
if(L->length == 0)
{
printf("顺序表已空,无法删除元素\n");
return;
}
//删除元素
for(int i = Position - 1;i < L->length - 1;i++)
L->Data[i] = L->Data[i + 1];
L->length--;
}
- 打印顺序表
- 判断顺序表是否为空
//6.打印顺序表
void Print1(List1 L)
{
//判断顺序表是否为空
if(L->length == 0)
{
printf("顺序表为空\n");
return;
}
//打印
for(int i = 0;i < L->length;i++)
printf("%d ",L->Data[i]);
printf("\n");
}
动态顺序表的相关操作
- 数据结构定义
- 定义了初始的长度,每次需要扩充,就每次增加扩容的长度,C++中STL的vector的实现规则与此类似
- 正常情况下,这样做速度很快,但是在遇到需要新增内存空间时,就会耗费时间,因为要新申请一片更大的空间,还要将之前存在的值一起复制到新的存储空间
#define INIT_SIZE 10 //动态顺序表的初始长度
#define INCREASE_SIZE 10 //动态顺序表每次扩容的长度
typedef int ElementType;
//动态顺序表
typedef struct Anode{
ElementType *Data; //通过动态分配内存来创建
int length; //目前的长度
int MaxSize; //目前的最大容量
}ASeqList;
typedef ASeqList* List2;
- 顺序表初始化
- 使用malloc()函数动态申请内存空间
//动态顺序表相关操作
//1.顺序表初始化
void Init2(List2 L)
{
L->length = 0;
L->Data = malloc(sizeof(ElementType)*INIT_SIZE); //初始化数组
L->MaxSize = INIT_SIZE;
}
- 顺序表扩容
- 在这里使用了realloc(void *ptr, size_t size)函数进行内存空间扩展
//2.顺序表扩容
void Extend(List2 L)
{
ElementType* Base = (ElementType*)realloc(L->Data,sizeof(ElementType)*INCREASE_SIZE);
if(!Base)
exit(-1);
L->Data = Base;
L->MaxSize = L->MaxSize + INCREASE_SIZE;
}
- 用数组为顺序表赋值
- 赋值之前也要判断空间是否足够,如果不够,就扩充空间
//3.用数组为顺序表赋值
void InsertByArray(List2 L,ElementType* Data,int Size)
{
if(L->MaxSize < Size)
Extend(L);
if(Size < 1)
{
printf("用于赋值的数组输入无效\n");
return;
}
for(int i = 0;i < Size;i++)
L->Data[i] = Data[i];
L->length = Size;
}
- 按位置找到对应的值
- 与静态顺序表类似
//4.按位置找到对应的值
ElementType Find_Ac_Position2(List2 L,int Position)
{
if(Position < 1 || Position > L->length)
{
printf("输入位置有误,请重新输入\n");
return -1;
}
return L->Data[Position - 1];
}
- 按值找到对应的位置
- 与静态顺序表类似
//5.按值查找位置
int Find_Position2(List2 L,ElementType value)
{
for(int i = 0;i < L->length;i++)
if(L->Data[i] == value)
return i + 1;
return -1;
}
- 插入数据
- 检查位置是否合法
- 检查顺序表是否已满,满了则扩容
//6.插入一个数据
void Insert2(List2 L,ElementType value,int Position)
{
//检查插入位置的合法性
if(Position < 1 || Position > L->length + 1)
{
printf("插入位置不合法\n");
return;
}
//检查顺序表是否已满
if(L->length == L->MaxSize)
{
Extend(L); //扩容
}
//插入元素
for(int i = L->length - 1;i >= Position - 1;i--)
L->Data[i + 1] = L->Data[i];
L->Data[Position - 1] = value;
L->length++;
}
- 删除数据
- 与静态顺序表类似
//7.删除一个数据
void Delete2(List2 L,int Position)
{
//检查删除元素位置的合法性
if(Position < 1 || Position > L->length)
{
printf("删除元素的位置有误,删除失败\n");
return;
}
//检查顺序表是否已经为空
if(L->length == 0)
{
printf("顺序表已空,无法删除元素\n");
return;
}
//删除元素
for(int i = Position - 1;i < L->length - 1;i++)
L->Data[i] = L->Data[i + 1];
L->length--;
}
- 打印顺序表
- 与静态顺序表类似
//8.打印顺序表
void Print2(List2 L)
{
//判断顺序表是否为空
if(L->length == 0)
{
printf("顺序表为空\n");
return;
}
//打印
for(int i = 0;i < L->length;i++)
printf("%d ",L->Data[i]);
printf("\n");
}
主函数
int main()
{
int Data[6] = {12,23,31,21,18,22};
printf("-----------静态顺序表操作------------\n");
printf("\n\n-------------------------------------\n");
printf("1.初始化顺序表\n");
List1 L = malloc(sizeof(SeqList));
Init1(L,Data,6);
Print1(L);
printf("\n\n-------------------------------------\n");
printf("2.按位置找到对应的值\n");
int value;
int Position;
printf("请输入要查找的位置: ");
scanf("%d",&Position);
value = Find_Ac_Position(L,Position);
if(value != -1)
printf("第%d位置的值为%d\n",Position,value);
printf("\n\n-------------------------------------\n");
printf("3.按值找到对应的位置\n");
printf("请输入要查询的值: ");
scanf("%d",&value);
Position = Find_Position(L,value);
if(Position == -1)
printf("没有这个值\n");
else
printf("%d在顺序表的第%d个位置\n",value,Position);
printf("\n\n-------------------------------------\n");
printf("4.插入一个值\n");
printf("请输入要插入的值: ");
scanf("%d",&value);
printf("请输入要插入的位置: ");
scanf("%d",&Position);
Insert1(L,value,Position);
printf("插入数据后的顺序表\n");
Print1(L);
printf("\n\n-------------------------------------\n");
printf("5.删除一个值\n");
printf("请输入要删除的值的位置: ");
scanf("%d",&Position);
Delete1(L,Position);
printf("删除数据后的顺序表\n");
Print1(L);
printf("-----------动态顺序表操作------------\n");
int Data1[10] = {12,23,31,21,18,22,11,9,14,25};
printf("\n\n-------------------------------------\n");
printf("1.初始化顺序表\n");
List2 L2 = malloc(sizeof(ASeqList));
Init2(L2);
InsertByArray(L2,Data1,10);
Print2(L2);
printf("\n\n-------------------------------------\n");
printf("2.按位置找到对应的值\n");
printf("请输入要查找的位置: ");
scanf("%d",&Position);
value = Find_Ac_Position2(L2,Position);
if(value != -1)
printf("第%d位置的值为%d\n",Position,value);
printf("\n\n-------------------------------------\n");
printf("3.按值找到对应的位置\n");
printf("请输入要查询的值: ");
scanf("%d",&value);
Position = Find_Position2(L2,value);
if(Position == -1)
printf("没有这个值\n");
else
printf("%d在顺序表的第%d个位置\n",value,Position);
printf("\n\n-------------------------------------\n");
printf("4.插入一个值\n");
printf("请输入要插入的值: ");
scanf("%d",&value);
printf("请输入要插入的位置: ");
scanf("%d",&Position);
Insert2(L2,value,Position);
printf("插入数据后的顺序表\n");
Print2(L2);
printf("\n\n-------------------------------------\n");
printf("5.删除一个值\n");
printf("请输入要删除的值的位置: ");
scanf("%d",&Position);
Delete2(L2,Position);
printf("删除数据后的顺序表\n");
Print2(L2);
return 0;
}
程序运行结果
- 静态顺序表
- 动态顺序表
全部代码下载链接:https://download.csdn.net/download/qq_43655213/12696029
参考文献
[1] 严蔚敏,吴伟民. 数据结构 (C语言版 ) [M]. 北京:清华大学出版社, 2011-07-01;
[2] 王道论坛,2021年数据结构考研复习指导[M]. 北京:电子工业出版社,2020.1