顺序表是在计算机内存中以数组的形式保存的线性表,是用一段物理地址连续的的存储单元依次存储数据元素的线性结构。
静态顺序表容量是定好的,很多操作会受限,这里仅展示动态顺序表:
typedef int SLDataType;
typedef struct SeqList
{
SLDataType* a;//指向动态开辟的数组
int size;//记录存储了多少个有效数据
int capicity;//空间容量大小
}SL;
下面是顺序表的几个基本操作
初始化:
void SLInit(SL* ps)
{
assert(ps);
ps->a=NULL;
ps->size=0;
ps->capacity=0;
}
销毁:
void SLDestroy(SL* ps)
{
assert(ps);
if(ps->a)
{
free(ps->a);
ps->a=NULL;
ps->size=ps->capicity=0;
}
}
打印:
void SLPrint(SL*ps)
{
assert(ps);
int i=0;
for(i=0;i<ps->size;i++)
{
printf("%d ",ps->a[i]);
}
printf("\n");
}
尾插(在顺序表末尾插入一个数):
void SLPushBack(SL* ps,SLDataType x)
{
assert(ps);
//扩容
if(ps->size==ps->capicity)
{
int newCapacity=ps->capacity==0?10:ps->capacity *2;
SLDataType* tmp=(SLDataType*)realloc(ps->a,newCapacity*sizeof(SLDatatype));
if(tmp==NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a=tmp;
pc->capacity=newCapacity;
}
ps->a[ps->size]=x;
ps->size++;
}
当size和Capacity值相等时,表已经满了,表尾没有地方可供我们插入数据了,考虑给表扩容,扩容这里用了一个三目运算符,判断表容量是否为零,不是,就在原容量上扩大二倍,如果原容量为零,表示这是一个刚初始化的表,0乘2还是0,为了避免这种情况,就暂时给它一个为10的容量值(这里的10也可以是别的数)。
使用realloc扩容时,注意realloc的返回类型是void*,这里就需要强制类型转换一下,把返回值类型转换成SLDataType*。realloc的作用是在指针a的位置开始,进行扩容,括号里的第二个参数newCapacity*sizeof(SLDataType)不是在原来的基础上再扩大的量,而是扩大后新容量是多少这里就写多少,申请成功后返回调整之后的内存块地址。申请失败会返回NULL,为防止失败后将一个空指针赋给a,我们可以先用一个指针tmp接收这个内存块的地址,这个tmp的作用就是判断realloc是否申请成功,tmp为空,perror报错,退出,tmp不为空,再将tmp赋给a。
原有数据是size个,从0到size-1,所以把需要插入的数据x放在size的位置上就好了,因为增加了一个,最后再给size加一。
尾删(删除顺序表尾部数据):
void SLPopBack(SL* ps)
{
assert(ps);
assert(ps->size>0);//检查一下表是否为空,不为空,程序继续执行,否则编译器报错
ps->size--;
}
用assert检查一下表是否为空,不为空,程序继续执行,否则编译器报错
头插(在顺序表头部插入一个数据):
void SLCheckCapacity(SL*ps)
{
assert(ps);
//扩容
if(ps->size==ps->capicity)
{
int newCapacity=ps->capacity==0?10:ps->capacity *2;
SLDataType* tmp=(SLDataType*)realloc(ps->a,newCapacity*sizeof(SLDatatype));
if(tmp==NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a=tmp;
pc->capacity=newCapacity;
}
}
void SLCheckCapacity(SL*ps);
void SLPushFront(SL* ps,SLDataType x)
{
//检查容量
assert(ps);
SLCheckCapacity(ps);
//挪动数据
int end=ps->size-1;
while(end>=0)
{
ps->a[size+1]=ps->a[size];
--end;
}
p->a[0]=x;
ps->size++;
}
在进行头插操作之前,先检查一下现有容量够不够我们再插入一个数据,这里就调用一个函数SLCheckCapacity(这个函数的具体解析在上一部分尾插中涉及,在上一部分是直接写进尾插函数中的,也可以把扩容单另写成函数SLCheckCapacity,这样就可以在尾插头插第一步检查容量时直接调用)。
挪动数据时,从后往前依次挪动。
头删(删除顺序表中第一个数据):
与头插相反,头删挪动数据时,从前往后依次挪动 。
void SLPopFront(SL*ps)
{
assert(ps);
assert(ps->size>0);
int begin=1;
while(begin<ps->size)
{
ps->a[begin-1]=ps->a[begin];
++begin;
}
ps->size--;
}
同样需要使用assert检查一下,表为空时,编译器会报错,否则继续进行下面的头删步骤。
在pos位置插入数据:
void SLInsert(SL*ps,int pos,SLDataType x)
{
assert(ps);
assert(pos>=0);
assert(pos<=ps->size);
SLCheckCapacity(SL*ps);
int end=ps->size-1;
while(end>=pos)
{
ps->a[end+1]=ps->a[end];
end--;
}
ps->a[pos]=x;
ps->size++;
}
用assert检查一下pos位置是否合法 。
当pos等于0时,相当于头插;当pos等于size时,相当于尾插,也就是说,该函数就可以替换上面的头插尾插函数了。
在pos位置删除数据:
void SLErase(SL*ps,int pos)
{
assert(ps);
assert(pos>=0);
assert(pos<ps->size);
int begin=pos+1;
while(begin<ps->size)
{
ps->a[begin-1]=ps->a[begin];
begin++;
}
ps->size--;
}
同理于SLInsert。
查找(查找数x在顺序表中的哪个位置):
void SLFind(SL*ps,SLDataType x)
{
assert(ps);
int i=0;
for(i=0;i<ps->size;i++)
{
if(ps->a[i]==x)
{
return i;
}
}
return -1;
}
在表中找到了x,返回位置,找不到,返回-1。
(P.S:代码中仅展示相关功能实现的语句,没有包含头文件,实际操作时一定不要忘记#include<stdio.h>、#include<stdilb.h>和#include< assert.h>)