一、概念
二、功能
1.尾插数据(在数组的尾部插入数据)
2.尾删数据(在数组的尾部删除一个数据)
3.头插数据(在数组的首部插入数据)
4.头删数据(在数组的首部删除一个数据)
5.中间插入(在数组的指定位置插入数据)
6.中间删除(删除数组指定位置的数据)
7.查找数据(搜索指定数据并返回数据的位置)
三、实现
3.1、定义结构体
包含数组a、size记录数据个数、capacity记录开辟空间的大小。
(这里我使用了typedef关键字定义结构为SL,方便后续写代码。
另外使用typedef定义了数据类型为SLDataType,如果后续需要更改储存数据类型,方便代码维护。)
代码:
typedef int SLDataType;//定义数据类型名称,方便代码维护
typedef struct Seqlist //typedef: 定义结构, 方便后续代码简单易懂
{
SLDataType* a; //指向动态开辟的数组
int size; //记录存储多少个有效数据
int capacity; //空间容量大小
}SL;
3.2、结构体初始化
一个形参:结构体地址的形参ps
首先将数组a的地址指向空指针(NULL)、size和capacity的值设置为0。
代码:
void SLInit(SL* ps)
{
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
3.3、尾插的实现
一个形参:结构体地址的形参ps尾插的实现需要两个步骤,首先应该判断当前开辟的栈的大小是否足够存放数据,在确保能够存放后再插入数据。
3.3.1、空间检查
一个形参:结构体地址的形参ps
空间检查很简单,只需要判断size的大小和capacity的大小是否相等、如果相等则证明当前开辟的栈已经满了,需要扩大。
int NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
这里使用了条件运算符,先判断空间大小是否为0,如果为0则赋值4,如果不为0则capacity乘2。
然后使用realloc函数开辟或扩大栈,开辟栈的大小为sizeof(SLDataType) * NewCapacity,返回的地址为tmp,注意要判断tmp是否指向空指针,栈开辟成功后再把tmp的地址赋予a。
代码:
void SLCapacityCheck(SL*ps)//检查栈大小是否够用
{
if (ps->size == ps->capacity)
{
int NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//判断空间大小是否为0,如果为0则赋值4,如果不为0则乘2
SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * NewCapacity);
if (tmp == NULL)
{
printf("perro");
exit(-1);
}
else
{
ps->capacity = NewCapacity;
ps->a = tmp;
}
}
}
3.3.2、插入数据
两个形参:结构体地址的形参ps、数据x
在空间检查后,只需要把原来数组大小基础上的加一位赋值为x即可。
size的值相应的加1。
代码:
void SLPushBack(SL* ps, SLDataType x)//尾插
{
assert(ps);//检查
SLCapacityCheck(ps);//检查空间
ps->a[ps->size] = x; //
ps->size++;
}
3.4、尾删的实现
一个形参:结构体地址的形参ps
首先可能想到的是让数组最后一位的值为0,但是当最后一位的值本来就为0时,好像无效,所以只需要把size的值减一,数组的最后一位省略即可。
最重要的是要判断size的值是否大于0,因为当size的值等于或小于0时,可能不会报错,但是不符合顺序表的结构。
void SLPopBack(SL* ps)//尾删
{
//ps->a[ps->size - 1]=0;//无效重置
assert(ps->size>0);//检查
ps->size--;
}
3.5、头插的实现
两个形参:结构体地址的形参ps、数据x
依旧先检查ps,然后利用以上尾插中实现的空间检查SLCapacityCheck函数检查栈的大小,然后把数组a内的位置往后挪一位,最后把首位赋值x即可。
注意不要忘了size值加一。
代码:
void SLPushFront(SL* ps,SLDataType x)
{
assert(ps);
SLCapacityCheck(ps);
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[0] = x;
ps->size++;
}
3.6、头删的实现
一个形参:结构体地址的形参ps
assert检查ps,数组a内每一个数据的位置往前移一位,size的值-1。
void SLPopFront(SL* ps)//首删
{
assert(ps);//检查
SLCapacityCheck(ps);
int begin = 0;
while (begin <= ps->size - 1)
{
ps->a[begin] = ps->a[begin + 1];
begin++;
}
ps->size--;
}
3.7、中间插入的实现
三个形参:结构体地址的形参ps、位置pos、数据x
首先检查ps、pos>=1(数据要插入的位置),pos<=size,确保插入位置在数组a内,并考虑插入位置在首位或者尾部的情况。
然后把将要插入的位置后的数据的位置往后移动一位,最后在该位置赋值x,size值+1即可。
void SLInsert(SL* ps, int pos, SLDataType x)//在pos位置插入数据
{
assert(ps);
assert(ps);
assert(pos >= 1);
assert(pos <= ps->size);
SLCapacityCheck;
int end = ps->size;
while (end>=pos)
{
ps->a[end] = ps->a[end-1];
end--;
}
ps->a[pos - 1] = x;//pos指的是第几个数据,所以用pos-1标记位置
ps->size++;
}
3.8.指定位置数据删除的实现
三个形参:结构体地址的形参ps、位置pos、数据x
首先检查ps、pos>=1(数据要插入的位置),pos<=size
然后从把删除的位置开始的数据从前往后替换为后一位的数据(往前移一位),size-1即可。
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(pos >= 1);
assert(pos <= ps->size);
int begin = pos - 1;
while (begin < ps->size - 1)
{
ps->a[begin] = ps->a[begin + 1];
begin++;
}
ps->size--;
}
3.9、查找函数的实现
三个形参:结构体的形参ps、需要查找的数据的形参x、考虑到有从某一位开始查找的需求,我引入了开始查找位置的形参begin。
首先检查ps,然后用for循环在begin到size范围内找第一个x值并返回它的位置。
int SLFind(SL* ps, SLDataType x,int begin)//begin开始查找位置
{
assert(ps);
for (int i = begin; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i+1;
}
}
return -1;
}
四、全部代码
(包含以上未提到的打印函数SLPrint和销毁函数SLDestory)
4.1 seqlist.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#pragma once//防指文件重定义
//动态顺序表 -- 按需扩空间
typedef int SLDataType;//定义数据类型名称,方便代码维护
typedef struct Seqlist //typedef: 定义结构, 方便后续代码简单易懂
{
SLDataType* a; //指向动态开辟的数组
int size; //记录存储多少个有效数据
int capacity; //空间容量大小
}SL;
void SLInit(SL* ps);
void SLPushBack(SL* ps, SLDataType x);//尾插
void SLPopBack(SL* ps);//尾删
void SLPushFront(SL* ps, SLDataType x);//首差
void SLPopFront(SL* ps);//首删
void SLDestory(SL* ps);//销毁
void SLPrint(SL* ps);//打印
void SLInsert(SL* ps, int pos, SLDataType x);//在pos位置插入数据
void SLErase(SL* ps, int pos);//在pos位置删除数据
int SLFind(SL* ps, SLDataType x,int begin);
4.2 seqlist.c
#include"Seqlist.h"
void SLPrint(SL* ps)
{
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
}
void SLInit(SL* ps)
{
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
void SLCapacityCheck(SL*ps)//检查栈大小是否够用
{
if (ps->size == ps->capacity)
{
int NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//判断空间大小是否为0,如果为0则赋值4,如果不为0则乘2
SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * NewCapacity);
if (tmp == NULL)
{
printf("perro");
exit(-1);
}
else
{
ps->capacity = NewCapacity;
ps->a = tmp;
}
}
}
void SLDestory(SL* ps)
{
if (ps->a != NULL)
{
free(ps->a);
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
}
void SLPushBack(SL* ps, SLDataType x)//尾插
{
assert(ps);
SLCapacityCheck(ps);
ps->a[ps->size] = x;
ps->size++;
}
//原地扩容/异地扩容
void SLPopBack(SL* ps)//尾删
{
//ps->a[ps->size - 1]=0;//无效重置
assert(ps->size>0);//检查
ps->size--;
}
void SLPushFront(SL* ps,SLDataType x)
{
assert(ps);
SLCapacityCheck(ps);
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[0] = x;
ps->size++;
}
void SLPopFront(SL* ps)//首删
{
assert(ps);//检查
SLCapacityCheck(ps);
int begin = 0;
while (begin <= ps->size - 1)
{
ps->a[begin] = ps->a[begin + 1];
begin++;
}
ps->size--;
}
void SLInsert(SL* ps, int pos, SLDataType x)//在pos位置插入数据
{
assert(ps);
assert(ps);
assert(pos >= 1);
assert(pos <= ps->size);
SLCapacityCheck;
int end = ps->size;
while (end>=pos)
{
ps->a[end] = ps->a[end-1];
end--;
}
ps->a[pos - 1] = x;//pos指的是第几个数据,所以用pos-1标记位置
ps->size++;
}
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(pos >= 1);
assert(pos <= ps->size);
int begin = pos - 1;
while (begin < ps->size - 1)
{
ps->a[begin] = ps->a[begin + 1];
begin++;
}
ps->size--;
}
int SLFind(SL* ps, SLDataType x,int begin)//begin开始查找位置
{
assert(ps);
for (int i = begin; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i+1;
}
}
return -1;
}
4.3 test.c
void meau()
{
printf("**********************************************************\n");
printf("*********1.尾插数据 2.头插数据****************\n");
printf("*********3.尾删数据 4.头删数据****************\n");
printf("*********5.中间插入 6.中间删除****************\n");
printf("*********7.打印 ****************\n");
printf("*********-1.退出 ****************\n");
printf("**********************************************************\n");
printf("**********************************************************\n");
}
int main()
{
SL s;
SLInit(&s);
int option = 0;
do {
meau();
printf("请输入命令:>");
scanf("%d", &option);
switch (option)
{
case 1:
printf("请依次输入你需要尾插的数据,以-1结束:>");
SLDataType val;
scanf("%d",&val);
while (val != -1)
{
SLPushBack(&s, val);
scanf("%d",&val);
}
break;
case 2:
printf("请依次输入你需要头插的数据,以-1结束:>");
int b;
scanf("%d", &b);
while (b != -1)
{
SLPushFront(&s, b);
scanf("%d", &b);
}
break;
case 3:
SLPopBack(&s);
printf("尾删成功!\n");
break;
case 4:
SLPopFront(&s);
printf("头删成功!\n");
break;
case 5:
printf("请依次输入你需要插入的位置和数据:>");
int pos = 0;
int x = 0;
scanf("%d", &pos);
scanf("%d", &x);
SLInsert(&s, pos, x);
printf("插入成功!\n");
break;
case 6:
printf("请输入你需要删除数据的位置:>");
int pos2= 0;
scanf("%d", &pos2);
SLErase(&s, pos2);
printf("删除成功!\n");
break;
case 7:
SLPrint(&s);
printf("\n");
break;
default:
break;
};
} while (option != -1);
printf("已退出!");
SLDestory(&s);
return 0;
}
注: test.c只做简单演示,SLFind函数已测试过,但未在test.c中引用,如果有需求自行引用实现功能。
五、代码演示
创作不易 本文章如果对你有帮助请点赞支持!
谢谢ღ