顺序表
静态顺序表
1.定义
用顺序存储的方式实现线性表顺序存储。把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。
如设第一个元素的存放为之是LOC(L),则第i个元素的的位置为LOC(L)+sizeof(ElemType),ElemType为元素类型。
2.实现方式
#define N 100
typedef in SQDataType;
struct SeqList
{
SQDataType a[N];
缺点:空间较为固定
一般不使用
动态顺序表
1.定义
typedef int SeqListDataType;
typedef struct SeqList
{
SeqListDataType* a;
int size;//记录存储多少个有效数据
int capacity;//空间容量大小
}SL;
2.实现方式
常识背景:SeqList.h放结构的定义和和函数的声明,SeqList.c放定义,test.c是运用。
此处重点介绍SeqList.c
此处详细介绍部分函数细节
由于这部分并不难,并且为了方便理解细节,博主直接将注释放在了代码块中,不再单独拿出来讲
a.数据的输出
void SeqListPrint(SL* ps)
{
assert(ps);
for (int i = 0; i < ps->size; ++i)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
b.空间的初始化和创建
void SeqListInit(SL* ps)
{
assert(ps);
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
c.空间的检查与扩容
void SeqListCheckCapacity(SL* ps)
{
assert(ps);
if (ps->size == ps->capacity)
{
//这里的4个int是博主自己设置的 目的是方便调试
int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SeqListDataType* tmp = (SeqListDataType*)realloc(ps->a, newCapacity * sizeof(SeqListDataType));
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capacity = newCapacity;
}
}
d.顺序表的销毁
void SeqListDestroy(SL* ps)
{
assert(ps);
//if(ps->a!=NULL)
if (ps->a)
{
free(ps->a);
//free只允许一次释放完所有空间,必须从申请位置开始释放,分段释放,空指针,野指针都会报错
//检查时是在运行时才会检查,每个编译器的检查位不同
//所以越界不一定会被检查出来,且如果在free处检查出来很大可能是其它地方出了问题
ps->a = NULL;
ps->size = ps->capacity = 0;
}
}
e.尾插
void SeqListPushBack(SL* ps, SeqListDataType x)
{
assert(ps);
SeqListCheckCapacity(ps);
//扩容
if (ps->size == ps->capacity)
{
int newCapacity = ps->capacity==0?4:ps->capacity * 2;
//realloc void* realloc(void*ptr,size_t size)
//ptr指向的是需要被扩容的空间,size是扩容后的大小
//如果第一个指针为空,则此时和malloc用法一样
//如果没有开辟空间,则会返回一个空指针
SeqListDataType* tmp = (SeqListDataType*)realloc(ps->a, newCapacity * sizeof(SeqListDataType));
//怕返回空指针,所以先用一个变量来接收
//如果是异地扩容,realloc内部会释放掉原来的空间,不需要考虑原地还是异地扩
//realloc一般会先考虑原地扩,一般大时不够再异地扩(一般情况,仅作了解)
//不用管是否拷贝数据!!!
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capacity = newCapacity;
}
ps->a[ps->size] = x;
ps->size++;
//直接用插入的函数也可以(这个函数在后面会实现)
//SeqListInsert(ps,ps->size,x);
}
f.尾删
void SeqListPopBack(SL* ps)
{
assert(ps);
//温柔的检查
//if (ps->size == 0)//防止越界访问
//{
// return;
//}
//ps->a[ps->size-1]=0;
//不需要改为0,因为有效个数值size已经改了,且在插入数据时也会直接覆盖
//暴力的检查:直接报错!!!!!!
assert(ps->size > 0);
ps->size--;
//此处函数会在后面实现
//SeqListErase(ps,ps->size-1);
}
g.头插
void SeqlListPushFront(SL* ps, SeqListDataType x)
{
assert(ps);
SeqListCheckCapacity(ps);
//挪动数据
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[0] = x;
ps->size++;//此点容易遗忘
//SeqListInsert(ps,0,x);
}
h.头删
//头删
void SeqListPopFront(SL* ps)
{
assert(ps);
assert(ps->size > 0);
//size为0时无法头删
SeqListCheckCapacity(ps);
int begin = 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
//直接复用删除
//SeqListErase(ps, 0);
}
i.任意有效位置插入
void SeqListInsert(SL* ps, int pos, SeqListDataType x)
{
assert(ps);
assert(pos >= 0);
assert(pos <= ps->size);
SeqListCheckCapacity(ps);
int end = ps->size-1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[pos] = x;
ps->size++;
}
j.任意有效位置删除
void SeqListErase(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0);
assert(pos < ps->size);
//assert(ps->size > 0);
//size>0的检查是没有必要的,因为第二和第三个assert就已经避免了这个情况
//挪动数据覆盖
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
begin++;
}
ps->size--;
}
k.查找某个数值
int SeqListFind(SL* ps, SeqListDataType x)
{
assert(ps);
for (int i = 0; i < ps->size; ++i)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
总结
这部分知识相信对于大家来说并不难,主要是一些细节上的掌握。
欢迎多给博主小白提提建议。