顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可分为:
define N 10 //长度
typedef int SLDatatype; //SLDatatype是数据类型,用宏,以防万一以后用的类型不一样问题
struct Seqlist
{
SLDatatype a[N]; //定长数组
int size; //计算有多少数据在这顺序表里面
};
静态顺序表的缺点:给小了不够用,给多了浪费,因此我不推荐用静态顺序表。
typedef int SLDatatype;
struct Seqlist
{
SLDatatype*a; //指向动态开辟的数组
int size; //有多少数据在这顺序表里面,有效数据的个数
int capacity; //容量空间的大小
};
接口实现
静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都使用动态顺序表根据需要动态的分配空间大小,所以下面我们实现动态顺序表。
实现之前我们先分别建三个工程。
写声明:
#progma once
#include
//动态顺序表的实现
typedef int SLDatatype; //这里typedef原因,类型也可以是double类型,还可以是其他类型,所以用typedef,这就是typedef的价值
typedef strcut Seqlist
{
SLDatatype*a;
int size; // 计算有几个数据
int capacity; //容量大小
}SL;
//初始化
void SLInit(SL* psl); //传结构体的地址
void SLDestroy(SL* psl); //传结构体的地址
//STL 命名风格
void SLPushBack(SL* psl, SLDatatype x); //尾插,尾部插入个数据
void SLPushFront(SL* psl, SLDatatype x);//头插,头部插入个数据
void SLPopBack(SL* psl);//尾删,尾部删除一个数据
void SLPopFront(SL* psl);//头删,头部删除一个数据
void SLInsert(SL* psl, int pos, SLDatatype x);//某个位置进行插入数据
void SLErase(SL* psl, int pos); //某个位置进行删除数据
void SLModify(SL* psl, int pos, SLDatatype x);//修改
int SLFind(SL* psl, SLDatatype x); //查找
void SLPrint(SL* psl); //打印函数
写顺序表无非就是写这么几个东西,初始化,增删查改,还有打印。
接下来,我要分别写出他们的实现。(Seqlist.c)
1:首先尾插的实现:
#include "Seqlist .h"
//Seqlist实现部分(Seqlist.c)
//写初始化部分
void SLInit(SL* psl) //接受结构体的地址
{
//psl->a = 0;//利用下面地址解引用就可以改变了,就可以初始化为0
psl->a = (SLDatatype*)malloc(sizeof(SLDatatype)*4); //也可以用malloc开辟点空间
//检查一下,是不是空
if(psl->a == NULL)
{
perror("malloc fail");
return;
}
psl->capacity = 4;
psl->size = 0;
}
void SLDestroy(SL* psl)
{
free(psl->a); //释放
psl->a = NULL; //置空
psl->size = 0;
psl->capacity = 0;
}
//尾插的实现
void SLPushBack(SL* psl,SLDatatype x)
{
SLCheckCapacity(psl); //容量不够,扩容函数
psl->a[psl->size] = x;
psl->size++;
}
//测试部分(test.c)
int main()
{
SL s;
SLInit(&s);
SLDestroy(&s) //把刚才地址给这
}
代码原理是这样的:
尾部size 位置加一个8,size++一下,再加一个9的时候就不行了,满了,再插就越界了,造成了空间不够。再把这个扩容函数加一下就ok了。
所以我们检查一下容量,为了以后头插也造成空间不够的现象,就得需要扩容。接下里就要单独先写一下扩容函数。
void SLCheckCapacity(SL* psl)
{
if(psl->size == psl->capacity) //当尾部size等于容量capacity就得扩容一下
{
SLDatatype* tmp = (SLDatatype*)realloc(psl->a,sizeof(SLDatatype)*psl->capacity*2);
//扩本来的二倍
if(tmp == NULL)
{
perror("realloc fail");
return ;
}
psl->a = tmp;
psl->capacity *= 2;
}
}
我们为了测试第一步尾插能不能正常运行,我接下里去test 函数里头测试一下,这个尾插函数。那接下来怎么测试呢,我们就写一个打印函数去测试一下。
打印函数:
void SLPrint(SL* psl)
{
for(int i = 0;isize;i++)
{
printf("%d ",psl->a[i]);
}
printf("\n");
}
int main()
{
SL S;
SLInit(&s);
SLPushBack(&s,1);
SLPushBack(&s,2); //这是我们写的尾插函数
SLPushBack(&s,3);
SLPushBack(&s,4);
SLPushBack(&s,5);
SLPrint(&s); //测试函数,先测试一下尾插函数写的怎么样
SLDestroy(&s);
return 0;
}
尾部插入数据就这么简单,这说明我们写的没问题,接下里按这个步骤把剩下的写下去。
2:头插的实现
头部插入数据就不一样,比较难一点。这里我们需要挪动数据,挪动要从后往前挪。
void SLPushFront(SL* psl, SLDatatype x);//头插,头部插入个数据
{
//头插数据之前我们同样监测一下,空间够不够的问题
//要是容量不足就扩容
SLCheckCapacity(SL* psl);
//挪动数据
int end = size-1;
while(end >= 0)
{
psl->a[end+1] = psl->a[end];
end--;
}
psl->a[0] = x;
psl->size++;
}
void TestSeqlist2() //写函数调用,比较整理
{
SL s;
SLInit(&s);
SLPushBack(&s,1);
SLPushBack(&s,2); //这是我们写的尾插函数
SLPushBack(&s,3);
SLPushBack(&s,4);
SLPushBack(&s,5);
SLPushFront(&S,7);
SLPushFront(&S,8);
SLPushFront(&S,9); //头部插入数
SLPushFront(&S,10);
SLPushFront(&S,11);
SLPprint(&s);
SLDestroy(&s);
}
int main()
{
TestSeqlist2(); //这里调用它
return 0;
}
3:尾删实现
void SLPopBack(SL* psl) //尾删,尾部删除一个数据
{
//为了数据已经空了,还在删,我们可以检查一下
//温柔检查
if(psl->size == 0)
return;
//暴力检查
assert(psl->size > 0); //断言
psl->size--;
}
4:头删实现
头删,就是把头部的数据删除掉,我们利用从前往后挪动的方法实现,先定义一个start 在0位置,每次把start+1赋给start位置,直到size-2的位置,不能大于size-1。
void SLPopFront(SL* psl) //头删,头部删除一个数据
{
//暴力检查
assert(psl->size>0);
int start = 0;
while(start < psl->size-1)
{
psl->a[start] = psl->a[start+1];
start++;
}
}
5:某个位置插入数据
void SLInsert(SL* psl, int pos, SLDatatype x) //某个位置进行插入数据
{
//暴力检查
assert(0 <= pos && pos <= psl->size);
//只要是插入数据,就得注意一下容量问题
SLCheckCapacity(psl);
int end = psl->size-1;
while(end >= pos)
{
psl->a[end+1] = psl->a[end];
end--;
}
psl->a[pos] = x;
psl->size++;
}
6:某个位置删除数据
void SLErase(SL* psl, int pos) //某个位置进行删除数据
{
//首先断言
assert(0 <= pos && pos <= psl->size);
int start = ppos+1;
while(start < psl->size)
{
psl->a[start-1] = psl->a[start];
start++;
}
psl->size--;
}
7:查找
int SLFind(SL* psl, SLDatatype x) //查找
{
for(int i = 0;isize;i++)
{
if(psl->a[i] == x)
{
return;
}
}
return -1;
}
8:修改
void SLModify(SL* psl, int pos, SLDatatype x) //修改
{
assert(0 <= pos && pos <= psl->size);
psl->a[pos] = x;
}
顺序表差不多就这么个回事,也可以简单的写一下菜单:
void menu()
{
printf("***************************\n");
printf("**1、尾插数据**2、尾删数据**\n");
printf("**3、头插数据**4、头删数据**\n");
printf("**5、打印数据**-1、退出** \n");
printf("***************************\n");
printf("***************************\n");
}
int main()
{
int option = 0;
SL s;
SLInit(&s);
while(option != -1) //当输入值不等于-1的时候可以说明没有退出程序,执行其他
{
menu();
printf("请输入你的操作:>");
scanf("%d",&option);
if(option == 1) //当1的时候 按照菜单执行尾插数据
{
int n = 0;
printf("请输入你要尾插的数据个数,依次输入要插入的数据:>"); //这儿给个提示,要写尾插的数据
scanf("%d",&n);
int x = 0; //再定义一个变量
while(n > 0) //
{
scanf("%d",&x);
SLPushBack(&s,x); //执行尾插数据
n--;
}
}
else if(option == 5) //5的时候打印看看
{
SLPrint(&s); /打印函数
}
else if(option == -1) //退出程序
{
break;
}
else
{
print("输入错误,请重新输入:>");
}
}
SLDestroy(&s);
return 0;
}
顺序表就这么结束了,如此简单!