顺序表详解(最后包含完整且成功测试的顺序表~需要可自取)
目录
1.顺序表概念
1.顺序表定义
用数组进行存储,数据从头逐个向后进行存储。
2.顺序表的分类
1.静态顺序表
这种顺序表直接定义了数组的大小,当数组填满时,不能继续增加元素,我们在存储数据的时候并不一定知道要存储多少数据,所以并不满足我们的要求。
2.动态顺序表
这种顺序表在定义的时候通常会使用指针来进行标记数据,通过移动指针来进行数据的增加和删除。动态顺序表比静态顺序表更加常用。也是我们要讨论的内容。
3.实现顺序表时函数的命名规则
函数的命名规则还是要根据驼峰规则的,起名的方式参考C++中STL库中函数的命名,方便记忆其中函数的功能。
2.实现顺序表最基本的函数
1.顺序表的结构体定义
typedef struct SeqList
{
int* a;//定义一个指向数组中元素的指针
int size;//定义当前数组的大小
int capacity;//定义数组的最大容量
}
2.打印顺序表
SeqListPrint(SL* ps)
{
int i;
for(i=0;i<ps->size;++i)
{
printf("%d",ps->a[i]);
}
printf("\n");
}
使i从第一个元素开始,向后遍历并打印。
3.判断顺序表是否已经满
void SeqListCheckCapacity(SL* ps,SLDataType x)
{
int newcapacity=ps->capacity==0?4:ps->capacity*2;//建立newcapacity使之为原来的二倍
if(ps->size==ps->capacity)
{
SLDataType* tmp=(SLDataType*)realloc(ps->a,newcapacity* sizeof(SLDataType));//扩充
newcapacity个数据的空间。
if(tmp==NULL)
{
printf("realloc fail");//扩充失败程序异常退出
exit(-1);
}
ps->a=tmp;接收扩充后的空间
ps->capacity=newcapacity;//将数组容量扩充为原来的二倍
}
当ps->size与ps->capacity相等的时候,说明顺序表已满,我们就需要对顺序表进行扩容,当进行第一次扩容的时候,ps->capacity是表示0的,我们先为它开辟四个字节的空间,在之后的扩容中,我们开辟的空间为原来空间的二倍大小。
在扩容过程中使用的是动态内存分配,即再开辟newcapacity大小的空间。当扩容失败时,realloc返回的是一个空指针,所以当tmp为空指针的时候扩容失败,exit(-1)异常退出程序。
最后返回新内存的首元素地址(和原来的一样),和新的capacity的值。
4.顺序表的销毁
void SeqListDestroy(SL* ps)
{
free(ps->a);//释放动态内存开辟的空间
pa->a=NULL;
pa->capacity=ps->size=0;
}
当我们要销毁顺序表的时候,我们需要首先释放空间,此时顺序表的头指针变成了一个野指针,我们需要将它赋值为NULL,然后将ps->capacity和ps->size赋值为0。
3.尾插与尾删
尾插与尾删,即在顺序表的末尾处对顺序表进行插入或者删除。
1.尾插
void SeqListPushBack(SL* ps,SLDataType x)
{
SeqListCheckCapacity(SL* ps);//检测顺序表是否已满
ps->a[pa->size]=x;
ps->size++;
}
当进行尾插时,首先把size的位置赋值为要插入的数据,然后再对size++,再插入数据,依次类推。
2.尾删
void SeqListPopback(SL* ps)
{
assert(ps->size>0);//当ps->size不大于0时程序报错退出
ps->size--;
}
当进行尾删的时候,只需要对size进行--操作就可以,当进行尾删之前,需要判断ps->size是否大于0,当ps->size==0的时候,说明顺序表中已经没有数据可以进行删除了,不能再对size进行--操作了。
4.头插与头删
即在顺序表的头处对顺序表进行插入或者删除。
1.头插
void SeqListPushFront(SL* ps,SLDataType x)
{
SeqListCheckCapacity(ps);//检测顺序表是否已满
int end=ps->size-1;
while(end>=0)
{
ps->a[end+1]=ps->a[end];//用前一个数据覆盖后一个数据
--end;
}
ps->a[0]=x;
ps->size++;
}
在顺序表头处进行插入数据,即需要将顺序表原有的数据同时向后移动一位,然后将要插入的数据进行插入,再将size++即可。
2.头删
void SeqListPopFront(SL* ps)
{
assert(ps->size>0);//断言判断顺序表是否为空
int begin=1;
while(begin<ps->size)
{
ps->a[begin-1]=ps->a[begin];//用后一个数据覆盖前一个数据
++begin;
}
ps->size--;
}
在进行头删的时候,首先需要判断顺序表是否为空,即使用断言语句assert(ps->size>0)当顺序表为空的时候,程序会报错。删除元素即将之后的所有元素逐个向前移动一位,将第一个元素进行覆盖即可,然后再进行size--。
5.在特定位置插入或删除数据
1.在pos处插入数据
void SeqListInsert(SL* ps;int pos;SLDataType)
{
assert(pos<=ps->size&&ps>=0);//判断pos位置是否合法
SeqListCheckCapacity(ps);//判断ps是否已满,已满则扩容
int end=ps->size-1;
while(end>=pos)
{
ps->a[end+1]=ps->a[end];//用从pos开始前一个数据覆盖后一个数据
--end;
}
ps->a[pos]=x;
ps->size++;
}
在向pos处插入数据的时候,首先需要判断pos是否合法,然后再检查顺序表是否已满,如果满了就要进行扩容,因此调用了SeqListCheckCapacity函数,之后将原来pos以及pos之后的元素都向后移动一位,再将x插入到pos的位置。
2.在pos处删除数据
void SeqListErase(SL* ps,int pos)
{
assert(pos>=0&&pos<ps->size);//判断pos是否合法
int begin=pos+1;
while(begin<ps->size)
{
ps->a[begin-1]=ps->a[begin];//从pos开始用后一个数据覆盖前一个数据
++begin;
}
ps->size--;
}
在pos处删除数据,首先判断pos是否合法,然后将pos之后的数据都向前移动一位,再将size--。
6.查找数据
int SeqListFind(SL*ps,SLDataType x)
{
int i;
for(i=0;i<ps->size;i++)
{
if(ps->a[i]==x)
{
return i;
}
}
return -1;//没找到返回-1
}
从开始向后查找,如果找到返回下标,没找到则返回-1。
7.全部代码即测试结果
1.seq.h文件
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#define SLDataType int
typedef struct List
{
SLDataType* a;
int size;
int capacity;
}SL;
void SeqListInit(SL* ps);
void SeqListDestroy(SL* ps);
void SeqListPrint(SL* ps);
void SeqListCheckCapacity(SL* ps);
void SeqListPushBack(SL* ps, SLDataType x);
void SeqListPopBack(SL* ps);
void SeqListPushFront(SL* ps, SLDataType x);
void SeqListPopFront(SL* ps);
void SeqListInsert(SL* ps, int pos, SLDataType x);
void SeqListErase(SL* ps, int pos);
int SeqListFind(SL* ps, SLDataType x);
2.seq.c文件
#include"seq.h"
void SeqListInit(SL* ps)//顺序表初始化
{
ps->a = NULL;
ps->size = ps->capacity = 0;
}
void SeqListDestroy(SL* ps)//顺序表销毁
{
free(ps->a);
ps->size = ps->capacity = 0;
}
void SeqListPrint(SL* ps)//顺序表打印
{
int i;
for (i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
void SeqListCheckCapacity(SL* ps)//顺序表检查是否已满,已满则扩容
{
if (ps->size == ps->capacity)
{
int newcapacity = (ps->capacity == 0 ? 4 : ps->capacity * 2);
SLDataType* temp = (SLDataType*)realloc(ps->a, newcapacity*sizeof(SLDataType));
if (temp == NULL)
{
printf("realloc failed\n");
exit(-1);
}
ps->a = temp;
ps->capacity = newcapacity;
}
}
void SeqListPushBack(SL* ps, SLDataType x)//顺序表尾插
{
SeqListCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
}
void SeqListPopBack(SL* ps)//顺序表尾删
{
assert(ps->size > 0);
ps->size--;
}
void SeqListPushFront(SL* ps, SLDataType x)//顺序表头插
{
SeqListCheckCapacity(ps);
int end = ps->size-1;
int i;
for (i = 0; i < ps->size; i++)
{
ps->a[end+1] = ps->a[end];
end--;
}
ps->a[0] = x;
ps->size++;
}
void SeqListPopFront(SL* ps)//顺序表头删
{
assert(ps->size > 0);
int begin = 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
void SeqListInsert(SL* ps, int pos, SLDataType x)//顺序表任意插入
{
assert(pos >= 0 && pos <= ps->size);
SeqListCheckCapacity(ps);
int end = ps->size-1;
while (end>=pos)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[pos] = x;
ps->size++;
}
void SeqListErase(SL* ps, int pos)//顺序表任意删除
{
assert(pos >= 0 && pos < ps->size);
int begin = pos+1;
while (begin < ps->size-1)
{
ps->a[begin-1] = ps->a[begin];
begin++;
}
ps->size--;
}
int SeqListFind(SL* ps, SLDataType x)//查找
{
int i;
for (i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
3.test.c文件
#include"seq.h"
void menu()
{
printf("***********************\n");
printf("**1.尾插 2.尾删****\n");
printf("**3.头插 4.头删****\n");
printf("**5.固定插入 6.固定删除\n");
printf("**7.查找 0.删除顺序表\n");
printf("**0.销毁并退出*********\n");
}
int main()
{
SL ps;
SeqListInit(&ps);
SeqListPushBack(&ps, 4);
SeqListPushBack(&ps, 5);
SeqListPushBack(&ps, 6);
SeqListPushFront(&ps, 3);
SeqListPushFront(&ps, 2);
SeqListPushFront(&ps, 1);
SeqListPrint(&ps);//顺序表初始化
int input;
do
{
menu();
printf("please input your choice:");
scanf("%d", &input);
SLDataType x;
int pos;
int find;
int getfind;
switch (input)//顺序表输入测试
{
case 0:
SeqListDestroy(&ps);
break;
case 1:
scanf("%d", &x);
SeqListPushBack(&ps, x);
SeqListPrint(&ps);
break;
case 2:
SeqListPopBack(&ps);
SeqListPrint(&ps);
break;
case 3:
scanf("%d", &x);
SeqListPushFront(&ps, x);
SeqListPrint(&ps);
break;
case 4:
SeqListPopFront(&ps);
SeqListPrint(&ps);
break;
case 5:
scanf("%d", &pos);
scanf("%d", &x);
SeqListInsert(&ps, pos, x);
SeqListPrint(&ps);
break;
case 6:
scanf("%d", &pos);
SeqListErase(&ps, pos);
SeqListPrint(&ps);
break;
case 7:
scanf("%d", &find);
getfind = SeqListFind(&ps, find);
printf("%d", getfind);
printf("\n");
break;
default:
printf("error input\n");
break;
}
} while (input);
return 0;
}
4.测试结果
1.尾插:测试成功
2.尾删:尾删至没有元素,程序退出,成功
3.头插:测试成功
4.头删:头删至没有元素,程序退出,成功
5.任意输入:进行了边缘检测和普通检测,成功
6.任意删除:进行了普通检测和越界检测,成功
7.查找位置:找到返回下标,没找到返回-1,成功
8.退出程序:测试成功
综上,程序测试成功!
8.总结
顺序表是学习数据结构的第一章节,其优点和缺点也是很明显的,由于是数据的连续存储所以访问速度极快,但数据的插入和删除就没有链表的方便,操作起来没有链表那样灵活,而且会造成内存分块过多,算是以空间换取时间的代表。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)