一.什么是顺序表?
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。其中顺序表又分为:静态顺序表和动态顺序表。
简单来说:顺序表:连续的物理空间存储---是数组 ,数据必须是从头开始,依次存储。
二.静态顺序表和动态顺序表的不同点
1. 静态顺序表:使用定长数组存储。
2. 动态顺序表:使用动态开辟的数组存储。
其中,因为动态顺序表是动态开辟的,所以最后还要释放(free)空间。下面我们来看看是怎么用静态顺序表进行增删查改操作的。至于动态顺序表,请看笔者的下一篇文章!
三.什么是静态顺序表
首先静态顺序表是用数组存储的,我们还要标识有多少个有效数字,所以我们可以定义一个结构体。
struct SeqList { int arr[50]; //数组用来存储数据 int size; //标识有效数据个数 };
为了方便后续更改数据类型以及数组的大小,我们用#define宏定义数组大小,typedef将数据类型进行重命名,这样的话以后要更改类型,或者更改数组大小只需更改一处位置即可,增加了程序的可维护性。
->
//注意:#define后面不加分号 //typedef后面加分号 #define Max_size 10 typedef int SeqlistType; typedef struct Seqlist { SeqlistType arr[Max_size]; //定长数组 SeqlistType size; //有效个数 }SQ;
四:函数接口实现
1.初始化结构体
起初没有有效数字,所以我们定义size为0,用menset初始化数组,按字节个数初始化。
注意事项:我们创建结构体变量,传参如果只传值,对形参的改变并不会影响实参,形参只是实参的一份临时拷贝。所以我们要传结构体变量的地址,用结构体指针接收。!!!
void SeqlistInit(SQ* s) { memset(s->arr, 0, sizeof(SeqlistType) * Max_size); s->size = 0; //最初没有有效数字 }
2.打印数据
注意:打印只需遍历数组即可,不会对结构体进行修改,所以传值,传址都可以!
方法1:传值
void SeqlistPrint(SQ s) { int i = 0; for (i = 0; i < s.size; i++) { printf("%d ", s.arr[i]); } printf("\n"); }
方法2:传址
void SeqlistPrint(SQ* s) { int i = 0; for (i = 0; i < s->size; i++) { printf("%d ", s->arr[i]); } }
3.头插数据
注意:因为数组是定长数组,所以插入数据时要先保证数组中还有空位置,要进行判断!
头插:即把数据放到arr[0]的位置,只需要把原来数组的数据向后移动即可。插入数据后,有效个数要记得+1.
注意:要从最后边开始移动,如果从前面开始移动,会造成把后面的数据覆盖掉。
void SeqlistPushFront(SQ* s,SeqlistType x) { //要保证不越界 if (s->size == Max_size) { printf("满了\n"); return ; } int i = 0; //把数据后移 for (i = s->size; i > 0; i--) { s->arr[i ] = s->arr[i-1]; } //插入 s->arr[0] = x; //有效数字+1 s->size++; }
4.尾插数据
同理,尾插也要保证数据还有空位置。
尾插数据只需把数据放入到数组的arr[ps->size]位置即可,数组的下标从0开始,而size是用来标识有效个数的,
插入前:
插入后:
>代码实现
//尾插 void SeqlistPushBack(SQ* s ,SeqlistType x) { //要保证不越界 if (s->size == Max_size) { printf("满了\n"); return; } s->arr[s->size] = x; s->size++; }
5.头删数据
头删:即去掉数组arr[0]的元素, ->将后面的元素向前覆盖。 这次不同于头插,这次要从前开始覆盖!!! 删除一个元素 ->size--
//头删 void SeqlistPopFront(SQ* s) { int i = 0; for (i = 0; i < s->size; i++) { s->arr[i] = s->arr[i+1]; } s->size--; }
6.尾删数据
因为size是标识有效个数的,用size来控制打印,只需要size--,就相当于把最后一位数据给去掉了,但最后一个数据还在数组空间内,但是我们已经访问不到了。
void SeqlistPopBack(SQ* s) { s->size--; }
注意事项:
1.传参是传值还是传地址的问题。
传值:形参是实参的一份临时拷贝,对形参的修改并不会影响实参。
传地址:对形参的修改就是对实参的修改。
2.插入数据前要先判断数组是否满了,否则会造成数据越界问题。
3.size是用来标识有效个数的,插入/删除数据后,size也要发生变化。
原码(含游戏菜单):
SeqList.h 文件总放函数声明以及类型重定义之类的
//
#define _CRT_SECURE_NO_WARNINGS 1 #pragma once //在.h文件中 #include<stdio.h> #include <string.h> //注意:#define后面不加分号 //typedef后面加分号 #define Max_size 10 typedef int SeqlistType; //创建结构体类型 typedef struct Seqlist { SeqlistType arr[Max_size]; //定长数组 SeqlistType size; //有效个数 }SQ; //要改变结构体内容就传地址,不改变内容传地址,传值都可以 //初始化结构体 void SeqlistInit(SQ* s); //打印结构体 void SeqlistPrint(SQ s); //头插 void SeqlistPushFront(SQ* s, SeqlistType x); //尾插 void SeqlistPushBack(SQ* s,SeqlistType x); //头删 void SeqlistPopFront(SQ* s); //尾删 void SeqlistPopBack(SQ* s);
在Seqlist.文件中实现接口函数
#define _CRT_SECURE_NO_WARNINGS 1 #pragma once #include"Seqlish.h" //初始化结构体 void SeqlistInit(SQ* s) { memset(s->arr, 0, sizeof(SeqlistType) * Max_size); s->size = 0; //最初没有有效数字 } //打印结构体 传值时 void SeqlistPrint(SQ s) { int i = 0; for (i = 0; i < s.size; i++) { printf("%d ", s.arr[i]); } printf("\n"); } 传地址时 //void SeqlistPrint(SQ* s) //{ // int i = 0; // for (i = 0; i < s->size; i++) // { // printf("%d ", s->arr[i]); // } //} //头插 void SeqlistPushFront(SQ* s,SeqlistType x) { //要保证不越界 if (s->size == Max_size) { printf("满了\n"); return ; } int i = 0; //把数据后移 for (i = s->size; i > 0; i--) { s->arr[i ] = s->arr[i-1]; } //插入 s->arr[0] = x; //有效数字+1 s->size++; } //尾插 void SeqlistPushBack(SQ* s ,SeqlistType x) { //要保证不越界 if (s->size == Max_size) { printf("满了\n"); return; } s->arr[s->size] = x; s->size++; } //尾删 void SeqlistPopBack(SQ* s) { s->size--; } //头删 void SeqlistPopFront(SQ* s) { int i = 0; for (i = 0; i < s->size; i++) { s->arr[i] = s->arr[i+1]; } s->size--; }
在test.c文件中测试接口有无问题 (含游戏菜单)
#define _CRT_SECURE_NO_WARNINGS 1 #pragma once #include"Seqlish.h" void menu() { printf("************************************\n"); printf("*******1.头插数据 2.尾插数据********\n"); printf("*******3.头删数据 4.尾删数据********\n"); printf("******** 0.exit ********\n"); } int main() { //定义结构体变量 SQ s1; SeqlistInit(&s1); int input = 0; int n = 0; do { menu(); printf("请输入你的选择->\n"); scanf("%d", &input); switch (input) { case 1:printf("进行头插操作,请输入你要插入的数字->\n"); scanf("%d", &n); //头插 SeqlistPushFront(&s1, n); printf("插入后顺序为:->"); SeqlistPrint(s1); break; case 2: printf("进行尾插操作,请输入你要插入的数字->\n"); scanf("%d", &n); //尾插 SeqlistPushBack(&s1, n); printf("插入后顺序为:->"); SeqlistPrint(s1); break; case 3 : if (s1.size > 0) { printf("进行头删操作,原顺序为:->"); SeqlistPrint(s1); printf("删除后->"); SeqlistPopFront(&s1); SeqlistPrint(s1); } else printf("请先插入数据\n"); break; case 4: if (s1.size > 0) { printf("进行尾删操作,原顺序为:->"); SeqlistPrint(s1); printf("删除后->"); SeqlistPopBack(&s1); SeqlistPrint(s1); } else printf("请先插入数据\n"); break; case 0: printf("退出成功\n"); break; default:printf("选择错误,重新选择\n"); break; } } while (input); }
下一篇博客,我将带大家深入了解动态顺序表~欢迎各位大佬关注。如有错误欢迎批评指正!