文章目录
定义
顺序表是线性表的顺序存储结构 ,其核心特征是:用一组地址连续的 存储单元依次 存储线性表中的元素,使得逻辑上相邻的元素在物理内存中也相邻。
如何理解这里的物理与逻辑 ?
①物理存储连续:所有元素存放在一片连续的内存空间中(类似数组).
例如:存储元素 [1,2,3] 的顺序表,在内存中可能占用地址 0x100 ~ 0x111(假设每个 int 占 4 字节),每个元素的地址依次递增 4 字节。
②逻辑与物理顺序一致:线性表中元素的逻辑顺序(即 "第 1 个、第 2 个...... 第 n 个")与它们在内存中的物理存储顺序完全相同。
例如:逻辑上的 "第 2 个元素",在物理内存中一定存储在 "第 1 个元素" 的后面、"第 3 个元素" 的前面.
代码实现
要实现顺序表的代码,不仅要实现顺序表的定义 ,还要完成一系列的增删改查的操作
定义
c
#define MAX_SIZE 100
//定义能够存储任意数据类型的数组 结构体
typedef struct SeqList
{
void* data[MAX_SIZE];//指针数组,里面全是指针数据元素
int size;
}SeqList;
之所以指针数组,是因为数组里面全部放的void*的指针,指针的大小是由操作系统决定的,这就意味着指针数组中的每个元素都是占8字节(X64电脑),但是指针具体指向的内容,可以是任意类型,这也是这个设计的美妙之处.这里不需要capacity,因为结构体的定义,已经写了指针数组的大小,相当于capacity了
知识点:
1.void* 是 通用指针类型(泛型指针)
2.它可以指向任意类型的数据
2.所有类型的指针都可以被隐式转换为 void*
附:
另一种定义方式,非指针数组:
c
typedef int SLdataType;
typedef struct Seqlist
{
SLdataType* a;
SLdataType size;
SLdataType capacity;
} SL; //声明这个结构体
创建
c
//创建顺序表
SeqList* create()
{
SeqList * SL = (SeqList*)calloc(1,sizeof(SeqList));
if (SL == NULL)
{
printf("create fail");
exit(0);
}
else
{
SL->size = 0;
return SL;
}
}
增加
c
void add(SeqList* SL,void* element)
{
if(! is_full)
{
SL->data[SL->size] = element;
SL->size++;
}else
{
printf("Seqlist is already full,you can not add element\n");
}
}
element就是要添加的指针,就像上面说的,这个指针可以是任意类型的指针,反正都只占8字节
查找
c
void* find_element(SeqList* SL,int index)
{
if(! is_empty(SL))
{
if (index < 0 || index >= SL->size )
{
printf("invalid index");
return NULL;
}else
{
void* temp = SL->data[index];
return temp;
}
}
}
根据下标去进行查找对应的元素,找到返回对应的指针
删除
c
//删除数据
int delete(SeqList* SL,int index)
{
if(is_empty(SL))
{
printf("There is no element\n");
return 0;
}
else if (index < 0 || index >= SL->size )
{
printf("invalid index");
return 0;
}else
{
for (size_t i = index; i < SL->size - 1; i++)
{
SL->data[i] = SL->data[i+1];
}
SL->size--;
return SL->size;
}
}
接受值是int,返回0代表删除失败,返回非0才代表删除成功
修改
c
int modify(SeqList *SL,int index,void* element){
//空数组无法修改
if (is_empty(SL))
{
printf("数组为空,修改失败!\n");
return 0;
}
//index必须有效
if (index<0 || index>=SL->size)
{
printf("无效index,请查证!\n");
return 0;//修改失败
}
//修改
SL->data[index]=element;
return 1;//修改成功
}
顺序表修改数据元素,找到index 直接覆盖
释放
c
//释放
void free_SL(SeqList *SL){
//不能释放NULL
if (SL)
{
free(SL);
}
}
一些功能函数
c
//2.判断顺序表是否为空
int is_empty(SeqList* SL){
return SL->size==0;
}
//3.判断顺序表是否已满
int is_full(SeqList* SL){
return SL->size>=MAX_SIZE;
}
主函数使用
c
```c
int main(int argc, char const *argv[])
{
//1.创建数组
array *SL=create();
//2.添加数据
int a=9527;
double b=7.77;
char c='U';
printf("&a=%p,&b=%p,&c=%p\n",&a,&b,&c);
add(SL,&a);
add(SL,&b);
add(SL,&c);
//2.通过index查询数据,返回数据的指针
int *ip=(int*)get_element(SL,0);
double *dp=(double*)get_element(SL,1);
char *cp=(char*)get_element(SL,2);
printf("ip=%p,dp=%p,cp=%p,*ip=%d,*dp=%lf,*cp=%c\n",ip,dp,cp,*ip,*dp,*cp);
//4.指定index删除元素
int res=delete(SL,2);
if (res)
{
int* ip2=(int*)get_element(SL,0);
ip=(int*)get_element(SL,1);
dp=(double*)get_element(SL,2);
cp=(char*)get_element(SL,3);
int* ip4=(int*)get_element(SL,4);
printf("ip2=%p,ip=%p,dp=%p,cp=%p,ip4=%p,*ip2=%d,*ip=%d,*dp=%lf,*cp=%c,*ip4=%d\n",ip2,ip,dp,cp,ip4,*ip2,*ip,*dp,*cp,*ip4);
}
//5.指定index修改元素
double h=9.99;
res=modify(SL,1,&h);
if (res)
{
ip2=(int*)get_element(SL,0);
double *dp2=(double*)get_element(SL,1);
dp=(double*)get_element(SL,2);
cp=(char*)get_element(SL,3);
ip4=(int*)get_element(SL,4);
printf("ip2=%p,dp2=%p,dp=%p,cp=%p,ip4=%p,*ip2=%d,*dp2=%lf,*dp=%lf,*cp=%c,*ip4=%d\n",ip2,dp2,dp,cp,ip4,*ip2,*dp2,*dp,*cp,*ip4);
}
//last.释放数组
free_SL(SL);
return 0;
}
我们写的delete(),modify()函数都使用int作为返回值,main()函数中也给出了如何合理运用这个整型返回值,那就是if (res)来查看是否删除或修改成功