前言
顺序表分为动态与静态,动态常用(可以改变数组大小,更合理的利用空间),故此处详解动态顺序表
动态顺序表函数
动态顺序表分不开三个定义
1数组元素
2有效个数
3最大容纳个数
创建文件并做好开始准备代码
创建文件
注意Seqlist.h是顺序表结构,声明顺序表的方法
Seqlist.c是实现顺序表的方法
test.c是测试
开始准备代码,在头文件写入
#pragma once #include <stdio.h> #include <stdlib.h> typedef int SLDateType; //此处进行重命名,是为了以后要进行更换数据类型便于更换 typedef struct SeqList { SLDateType* arr; int size; int capacity;//空间大小 }SL;//此处再次进行重命名,是为了后续方便后续
顺序表的初始化
必须使用指针传参,若使用传值,则会出现未初始化
- 在test.c的代码 后续在test.c加上代码只需在SLTest01加上函数名(&s1)
#include "Seqlist.h" void SLTest01() { SL s1; SLInit(&s1);//后续在test.c加上代码只需在SLTest01加上函数名(&s1) } int main() { SLTest01(); return 0; }
- 在Seqlist.c的代码
#include "Seqlist.h" void SLInit(SL*ps) { ps->arr = NULL; ps->size = ps->capacity = 0; }
- 在Seqlist.h的代码
#pragma once #include <stdio.h> #include <stdlib.h> typedef int SLDateType; //此处进行重命名,是为了以后要进行更换数据类型便于更换 typedef struct SeqList { SLDateType* arr; int size; int capacity;//空间大小 }SL;//此处再次进行重命名,是为了后续方便后续 //顺序表初始化 void SLInit(SL* ps);
顺序表的销毁
此处简单,先讲
Seqlist.h的代码
//本来应是顺序表的插入,但销毁简单,所以先讲 void SLDestory(SL* ps);
Seqlist.c的代码
(free只能释放动态开辟的空间,等下在后面开辟空间会讲解,不要认为此处错了)
void SLDestory(SL* ps) { if (ps->arr != NULL) { free(ps->arr); } ps->arr = NULL; }
test.c
void SLTest01() { SL s1; SLInit(&s1);//后续在test.c加上代码只需在SLTest01加上函数名(&s1) SLDestory(&s1); }
后续Seqlist.h与Seqlist.c的代码加入形式不会与上面加入差距太大,都是函数名(接受的值)
故下面没有特殊情况(或编译代码时)不进行展示
顺序表的插入
共有三种插入方式
从尾部插入
即再数组的尾部插入一个数
直接给出结论,计算机扩大空间一般是2倍/3倍,此处以扩2倍为例
在顺序表插入之前,要先判断空间是否足够
如果不足,要开辟空间,这才能够进行插入,又因为不确定数据的大小,所以是动态开辟看,且使用realloc函数进行开辟
Seqlist.h的代码
//顺序表的尾插 void SLPushBack(SL* ps,SLDatetype x);
test.c的代码
表示尾插一个数字为4
//尾插 SLPushBack(&s1,4);
Seqlist.c的代码
判断空间是否足够和空间的开辟
void SLPushBack(SL* ps,SLDateType x) { assert(ps);//先进行断言,防止NULL为空指针 if (ps->size = ps->capacity) { int newcapacity = ps->capacity == NULL ? 4 : 2 * sizeof(ps->capacity); SLDateType* temp = (SLDateType*)realloc(ps->arr, 2 * newcapacity); if (temp == NULL) { perror("realloc"); exit(1); } //空间申请成功 ps->capacity = newcapacity; ps->arr = temp; } }
当后面的两种尾插还要再写一次,过于繁琐,不如再创建一个判断空间是否足够和开辟空间的函数
此后,只需调用函数即可
void SLApply(SL* ps) { if(ps->size == ps->capacity) { int newcapacity = ps->capacity == NULL ? 4 : 2 * sizeof(ps->capacity); SLDateType* temp = (SLDateType*)realloc(ps->arr, sizeof(SLDateType) * newcapacity); if (temp == NULL) { perror("realloc"); exit(1); } //空间申请成功 ps->capacity = newcapacity; ps->arr = temp; } } void SLPushBack(SL* ps,SLDateType x) { assert(ps);//先进行断言,防止NULL为空指针 SLApply(ps); }
最后再进行插入
注意,插入一个数据,有效数据size也要++
void SLPushBack(SL* ps,SLDateType x) { assert(ps);//先进行断言,防止NULL为空指针 SLApply(ps); ps->arr[ps->size++] = x;//注意,插入了一个数据,有效数组扩大一位,size要++ }
顺序表的打印
如果只是一次的插入,那我们可以清晰的了解过程
但若是以下情况甚至更多呢?
include "Seqlist.h" void SLTest01() { SL s1; SLInit(&s1);//后续在test.c加上代码只需在SLTest01加上函数名(&s1) //尾插 SLPushBack(&s1,1); SLPushBack(&s1,2); SLPushBack(&s1,3); SLPushBack(&s1,4); SLPushBack(&s1,5); SLDestory(&s1,6); } int main() { SLTest01(); return 0; }
- 此时有两种解决方法
1是Sleep函数
2是每插入都打印顺序表
此处采用方法2
则在test.c加入
此处使用的是顺序表的原始数据,所以传值即可
//尾插 SLPushBack(&s1,1); SLprint(s1); SLPushBack(&s1,2); SLprint(s1); SLPushBack(&s1,3); SLprint(s1); SLPushBack(&s1,4); SLprint(s1); SLPushBack(&s1,5); SLprint(s1); SLDestory(&s1,6); SLprint(s1);
在Seqlist.c加入
void SLprint(SL ps) { for (int i = 0; i < ps.size; i++) printf("%d ", ps.arr[i]); printf("\n"); }
接下来让我们运行代码,看看效果
结果如下,正确
从头部插入
从头部插入,经过画图知,即arr里的数据,全都要往后移一位,且是从最后一个数据开始移
实现方法有两种
- for循环
- 使用memmove函数
此处使用方法二
Seqlist.h的代码
//顺序表的头插 void SLPushFront(SL* ps, SLDateType x);
test.c的代码
SLPushFront(&s1, 6); SLprint(s1); SLPushFront(&s1, 7); SLprint(s1);
seqlist.c的代码
void SLPushFront(SL* ps, SLDateType x) { assert(ps); SLApply(ps); memmove(ps->arr + 1, ps->arr, sizeof(SLDateType) * ps->size); ps->arr[0] = x;//再进行插入 ++ps->size;//此时size依旧要++ }
最后结果,预取一样
从指定位置插入
指定位置插入,即比前面多了一个插入位置的下标,再分析其实就是将尾插的插入与头插的挪位置结合起来
同理:挪位置与头插一样有两种方式
此处依然使用memmove函数
Seqlist.h的代码
int num表示插入位置的下标
//顺序表的指定位置插入 void SLPushNoknow(SL* ps,int num,SLDateType x);
test.c的代码
SLPushNoknow(&s1, 4, 3); SLprint(s1); SLPushNoknow(&s1, 1, 1); SLprint(s1);
Seqlist.c的代码
void SLPushNoknow(SL* ps, int num, SLDateType x) { assert(ps); assert( num>=0 && num <= ps->size);//此处需判断插入位置是否在有效数字之间,此处num = size也可直接开辟空间 SLApply(ps); memmove(ps->arr + num + 1, ps->arr + num, sizeof(SLDateType) * (ps->size - num )); ps->arr[num] = x; ++ps->size; }
插入全部完成,接下来进入删除
从尾部删除
本质就是有效数字size减少一个,无法读取即可
Seqlist.h的代码
//顺序表的尾删 void SLCleanBack(SL* ps);
test.c的代码
//顺序表的尾删 void SLCleanBack(SL* ps);
Seqlist.c
void SLCleanBack(SL* ps) { assert(ps); assert(ps->size);//加入判断有效数字是否为0,不为0才可删 //尾删其实就是有效数字少一个即可 ps->size--; }
从头部删除
头部删除与尾部删除大致一样,只是头部删除多了一个挪动数据的步骤
同样:此处依然可以采用for循环和memmove来挪动元素,
此处依旧采用memmove
SLeqlist.h
//顺序表的头删 void SLCleanFront(SL* ps);
test.c
SLCleanFront(&s1); SLprint(s1); SLCleanFront(&s1); SLprint(s1);
Seqlist.c
void SLCleanFront(SL* ps) { assert(ps); assert(ps->size); memmove(ps->arr, ps->arr + 1, sizeof(SLDateType) * ps->size - 1); ps->size--; }
从指定位置删除
与前面添加一样,指定位置删除,其实也是结合了头删和尾删,一样需要判断输入的下标是否有效
Seqlist.h
//顺序表的指定删除 void SLCleanNoknow(SL* ps,int n);
test.c
//指定位置删除 SLCleanNoknow(&s1, 1); SLprint(s1); SLCleanNoknow(&s1, 0); SLprint(s1);
Seqlist.c
void SLCleanNoknow(SL* ps, int n) { assert(ps); assert(ps->size); assert(n >= 0 && n < ps -> size); memmove(ps->arr + n, ps->arr + n + 1, sizeof(SLDateType) * (ps->size - n - 1)); ps->size--; }
顺序表查找数据
在顺序表中查找一个数字,成功则返回该数字的下标,没有则返回:找不到
Seqlist.h
//顺序表的查找 int SLFind(SL* ps, int n);
test.c
//查找数据 int find = SLFind(&s1, 10); if (find < 0) printf("找不到\n"); else printf("找到了,下标为%d\n", find);
Seqlist.c
/如果没有,返回值应该小于0,因为下标大于等于0
int SLFind(SL* ps, int n) { assert(ps); for (int i = 0; i < ps->size; i++) { if (ps->arr[i] == n) return i; } return -1;//如果没有,返回值应该小于0,因为下标大于等于0 }
最后附上全部代码
Seqlist.h
#pragma once #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string.h> typedef int SLDateType; //此处进行重命名,是为了以后要进行更换数据类型便于更换 typedef struct SeqList { SLDateType* arr; int size; int capacity;//空间大小 }SL;//此处再次进行重命名,是为了后续方便后续 //顺序表初始化 void SLInit(SL* ps); //本来应是顺序表的插入,但销毁简单,所以先讲 void SLDestory(SL* ps); //顺序表的尾插 void SLPushBack(SL* ps, SLDateType x); //顺序表的头插 void SLPushFront(SL* ps, SLDateType x); //顺序表的指定位置插入 void SLPushNoknow(SL* ps,int num,SLDateType x); //顺序表的头删 void SLCleanFront(SL* ps); //顺序表的尾删 void SLCleanBack(SL* ps); //顺序表的指定删除 void SLCleanNoknow(SL* ps,int n); //顺序表的查找 int SLFind(SL* ps, int n);
test.c
#include "Seqlist.h" void SLTest01() { //创建数据表 SL s1; SLInit(&s1);//后续在test.c加上代码只需在SLTest01加上函数名(&s1) //尾插 SLPushBack(&s1, 1); SLprint(s1); SLPushBack(&s1, 2); SLprint(s1); SLPushBack(&s1, 3); SLprint(s1); SLPushBack(&s1, 4); SLprint(s1); SLPushBack(&s1, 5); SLprint(s1); //头插 SLPushFront(&s1, 6); SLprint(s1); SLPushFront(&s1, 7); SLprint(s1); SLPushNoknow(&s1, 3, 3); SLprint(s1); SLPushNoknow(&s1, 1, 1); //尾删 SLCleanBack(&s1); SLprint(s1); SLCleanBack(&s1); SLprint(s1); //头删 SLCleanFront(&s1); SLprint(s1); SLCleanFront(&s1); SLprint(s1); //指定位置删除 SLCleanNoknow(&s1, 1); SLprint(s1); SLCleanNoknow(&s1, 0); SLprint(s1); //查找数据 int find = SLFind(&s1, 10); if (find < 0) printf("找不到\n"); else printf("找到了,下标为%d\n", find); //顺序表销毁 SLDestory(&s1); } int main() { SLTest01(); return 0; }
Seqlist.c
#include "Seqlist.h" void SLInit(SL*ps) { ps->arr = NULL; ps->size = ps->capacity = 0; } void SLDestory(SL* ps) { if (ps->arr != NULL) { free(ps->arr); } ps->arr = NULL; ps->size = ps->capacity = 0; } void SLprint(SL ps) { for (int i = 0; i < ps.size; i++) printf("%d ", ps.arr[i]); printf("\n"); } void SLApply(SL* ps) { if(ps->size == ps->capacity) { int newcapacity = (ps->capacity == NULL ? 4 : 2 * ps->capacity); SLDateType* temp = (SLDateType*)realloc(ps->arr, sizeof(SLDateType) * newcapacity); if (temp == NULL) { perror("realloc"); exit(1); } ps->capacity = newcapacity; ps->arr = temp; } } void SLPushBack(SL* ps,SLDateType x) { assert(ps);//先进行断言,防止ps为空指针 SLApply(ps); ps->arr[ps->size++] = x;//注意,插入了一个数据,有效数字扩大一位,size要++ } void SLPushFront(SL* ps, SLDateType x) { assert(ps); SLApply(ps); memmove(ps->arr + 1, ps->arr, sizeof(SLDateType) * ps->size); ps->arr[0] = x;//再进行插入 ++ps->size;//此时size依旧要++ } void SLPushNoknow(SL* ps, int num, SLDateType x) { assert(ps); assert( num>=0 && num <= ps->size);//此处需判断插入位置是否在有效数字之间,此处num = size也可直接开辟空间 SLApply(ps); memmove(ps->arr + num + 1, ps->arr + num, sizeof(SLDateType) * (ps->size - num )); ps->arr[num] = x; ++ps->size; } void SLCleanBack(SL* ps) { assert(ps); assert(ps->size);//加入判断有效数字是否为0,不为0才可删 //尾删其实就是有效数字少一个即可 ps->size--; } void SLCleanFront(SL* ps) { assert(ps); assert(ps->size); memmove(ps->arr, ps->arr + 1, sizeof(SLDateType) * ps->size - 1); ps->size--; } void SLCleanNoknow(SL* ps, int n) { assert(ps); assert(ps->size); assert(n >= 0 && n < ps -> size); memmove(ps->arr + n, ps->arr + n + 1, sizeof(SLDateType) * (ps->size - n - 1)); ps->size--; } int SLFind(SL* ps, int n) { assert(ps); for (int i = 0; i < ps->size; i++) { if (ps->arr[i] == n) return i; } return -1;//如果没有,返回值应该小于0,因为下标大于等于0 }