一、顺序表的定义
二、顺序表的编写
1. 顺序表的初始化和删除
2.顺序表的打印
3.顺序表的增删
4.顺序表的查改
三、顺序表的总代码
一、顺序表的定义
顺序表是线性表的一种,它是用一段物理地址连续的存储单元依次存储数据元素的线性结构,顺序表的底层逻辑其实是数组,所以顺序表一般用数组存储,并且是在数组上完成增删查改。
顺序表分为静态顺序表和动态顺序表,静态顺序表里面的元素个数是固定的,如果元素个数给小了会导致其余数据丢失,如果元素个数给大了会导致过剩,浪费空间,而动态顺序表就很好的解决了这个问题。
首先我们思考一个顺序表,它的底层结构是数组,所以说我们肯定要定义一个数组,其次我们还需要定义一个size来表示顺序表中有效的数字个数,另外还需要一个capacity来表示顺序表容量空间的大小,这时候我们就需要一个结构体,将这三个包含起来。
现在我们来尝试写代码,首先定义一个头文件seq.h,它的作用是表达顺序表的结构以及声明顺序表的方法;接着还要一个源文件seq.c,它的作用是实现顺序表的方法,另外一个源文件是test.c,它用来测试我们的代码,我们可以将所有的预处理指令放在seq.h里面,这样子的话两个源文件只需要引用
#include “seq.h”
现在我们在头文件里面表达顺序表的结构
我们发现这个代码是不完美的,首先我们可以将结构体重命名,利用typedef将它重命名为SL,这样子更方便使用;其次,我们发现这个顺序表只能用于int结构,所以说我们可以将int结构也使用typedef重命名,这样子就不再局限于int,改进代码如下:
二、顺序表的编写
1. 顺序表的初始化和删除
我们要初始顺序表,可以自己编写一个函数,SLInit,那我们要将顺序表传入这个函数,那么要使用传址调用,因为如果使用传值调用,函数调用完之后,形参会被释放,返回主函数之后,我们发现顺序表没有变化,因此要用传址调用。
初始化将这些置为空和0,注意这里是传地址,所以结构体调用不能用.符号,要用->
下一步我们进行顺序表的销毁,使用SLDestroy函数:
注意:这里首先判断arr空间是否为空,如果不是,要先用free函数free掉。
2.顺序表的打印
顺序表的打印很简答,只需要遍历函数,并且这里没有修改数据,所以并不需要使用传址调用,我们使用SLPrint函数:
3.顺序表的增删
顺序表的增加有头插和尾插,删除同样也有头删和尾删
我们先来写头插和尾差,尾插相对容易一点,但是我们需要一个前置函数checkcapacity,我们如果判断size==capacity,那就代表没有空间了,就需要扩容,那就让capacity*2,但是一开始size和capacity都是0,所以我们要判断,如果一开始是0,就给他4的空间,否则就让capacity*2,这里可以使用三目操作符,另外扩容是额外开辟空间,所以用realloc,具体代码如下:
前置函数完成,现在开始对顺序表进行尾插和头插:
顺序表的插入完成以后,我们来实现顺序表的头删和尾删
尾删很简单,直接让size--就好了,头删稍微复杂一点,需要让第二位开始每一个元素往前移一位:
头删如下:
4.顺序表在指定位置的查改
这里主要使用三个函数,SLInsert用于在指定位置插入数据,所以要调用顺序表,给定位置pos,插入的数据x;SLErase用于删去指定位置的数据,SLFind用于找到指定位置的数据。
首先是SLInsert函数:
SLErase函数:
SLFind函数:
这里我们要在test.c文件里写一下代码接受一下SLFind函数的返回值:
三、顺序表的总代码
1.seq.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int SLdata;
typedef struct seqlist
{
SLdata* arr;
int size;
int capacity;
}SL;
void SLInit(SL * ps);
void SLDestroy(SL* ps);
void SLPushBack(SL* ps, SLdata x);
void SLPushFront(SL* ps, SLdata x);
void SLprint(SL s);
void SLPopBack(SL* ps);
void SLPopFront(SL* ps);
void SLInsert(SL* ps, int pos, SLdata x);
void SLErase(SL* ps, int pos);
int SLFind(SL* ps, SLdata x);
2.seq.c
#include"seq.h"
void SLInit(SL* ps)
{
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
void SLDestroy(SL* ps)
{
if (ps->arr)
{
free(ps->arr);
}
ps->arr = NULL;
ps->size = ps->capacity = 0;
}
void SLPrint(SL s)
{
for (int i = 0;i < s.size;i++)
{
printf("%d ", s.arr[i]);
}
printf("\n");
}
void checkcapacity(SL* ps)
{
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity = 0 ? 4 : 2 * ps->capacity;
//三目操作符来扩容空间,为了防止代码错误,先使用newcapacity接收
SLdata* tmp = (SLdata*)realloc(ps->arr, newcapacity * sizeof(SLdata));
//这里使用临时变量tmp来接受扩容后的数组,进行验证再赋值给ps->arr
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
else
{
//验证完毕没有问题,进行赋值
ps->arr = tmp;
ps->capacity = newcapacity;
}
}
}
void SLPushBack(SL* ps, SLdata x)
{
assert(ps);
//断言防止顺序表为空或者非法输入
checkcapacity(ps);
//给顺序表扩容
ps->arr[ps->size++] = x;
//将x插在顺序表尾部,记得要让ps->size++
}
void SLPushFront(SL* ps, SLdata x)
{
assert(ps);
//断言防止顺序表为空或者非法输入
checkcapacity(ps);
//给顺序表扩容
for (int i = ps->size;i > 0;i--)
{
ps->arr[i] = ps->arr[i - 1];
}
//将x插在顺序表头部,利用循环让每一个元素往后移一位
ps->arr[0] = x;
ps->size++;
//记得要让ps->size++
}
void SLPopBack(SL* ps)
{
assert(ps);
assert(ps->size);
//检测顺序表是否为空
ps->size--;
//将最后一个位置的元素删掉
}
void SLPopFront(SL* ps)
{
assert(ps);
assert(ps->size);
//检测顺序表是否为空
for (int i = 0;i < ps->size - 1;i++)
{
ps->arr[i] = ps->arr[i + 1];
}
//循环将第一个元素覆盖掉
ps->size--;
//删掉一个元素,size减少1
}
3.test.c
#include "seq.h"
void test01()
{
SL s1;
SLInit(&s1);
SLPushBack(&s1, 1);
SLPushBack(&s1, 2);
SLPushBack(&s1, 3);
SLPushBack(&s1, 4);
SLPushBack(&s1, 5);
SLprint(s1);
int find = SLFind(&s1, 4);
if (find < 0)
{
printf("没有找到\n");
}
else
{
printf("找到了,下标是%d\n", find);
}
SLDestroy(&s1);
}
int main()
{
test01();
return 0;
}