写在前面
这是我们学习数据结构的第一步,万事开头难,我把我在学习之中遇到的种种问题渗透在讲解之中,文章的内容分为几个部分,希望读者能快速了解文章的脉络架构:
顺序表的存储结构——即用结构体定义
顺序表的基本操作——增删改查
顺序表的应用——完整的可执行程序(利用前面的基本操作)
先学思想
顺序表的存储结构
#define OK 1
#define ERROR 0
#define MAXSIZE 100
typedef int Status;
typedef int ElemType;//把int重命名
//顺序表存储结构
typedef struct
{
ElemType data[MAXSIZE];
int length;
}SqList;
上一篇文章的时候,我们讲解了typedef的使用,那么使用typedef就有它的好处
ElemType 实际上就是int,status实际上也是int,那么我们在更改数据类型char的时候,只要在这一行代码的地方把int改为char,就能很方便的更改,另外在下面代码的地方遇到这几个陌生单词,要记得哟
顺序表的基本操作
插入:说白了就是向数组内插入一个元素,直接插入在数组的末尾还好,若插入在数组的前面或者中间,可不敢直接就插入,我们得先把后面的元素都向后移动一个位置(前提是数组足够大),再将数据插入对应的位置
//插入,在L中的第i个位置之前插入新的数据——时间复杂度为O(n)
//小提示:SqList* L和SqList L什么场景,需要哪种选择?
//我们主要看这个传递的参数是否需要改变,详细的解答我们放到下面讲,先不要管参数带不带*号
Status InsertElem(SqList* L, int i, ElemType e)
{
int k;
//不考虑扩容,如果线性表满了,就没必要插入了
if (L->length == MAXSIZE)
return ERROR;
/*如果查找的数据不在线性表的范围内,不能查找
注意:我们通常将顺序表的第一个元素编号为1,放在数组下标为0的位置上,其他元素顺延*/
if (i<1 || i>L->length)
return ERROR;
//若插入数据不在表尾
if (i <= L->length)
{
//将要插入位置后数据元素向后移动一位,k是下标
for (k = L->length - 1; k >= i - 1; k--)
L->data[k + 1] = L->data[k];
//将新元素插入
L->data[i - 1] = e;
}
L->length++;//将计数器+1
return OK;
}
删除:删除数组的元素也是十分简单的,要注意的是数组为空,或者要删除的位置很奇葩,就没必要执行这段程序了,删除和插入有点类似,从要删除的位置开始将后面的元素依次向前覆盖......SqList* L,ElemType* e这样的参数还是放到本篇文章的后面讲。
//删除元素
Status DeleteElem(SqList* L, int i, ElemType* e)
{
int k;
//表为空,或者查找的数据不在表范围,则返回错误
if (L->length == 0 || i<1 || i>L->length)
return ERROR;
*e = L->data[i - 1];//记录要删除的数据
//如果删除元素不在表尾
if (i < L->length)
{
for (k = i; k < L->length; k++)
L->data[k - 1] = L->data[k];
}
L->length--;//记录表长的变量-1
return OK;
}
修改和查找就更简单了,就写在下面的代码中了,我们下面就直接对上面的代码进行调用
再学应用
这段代码利用上面所学的知识,进行了完整的调用,可以在VS2022上运行
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<time.h>
#define MAXSIZE 100
#define OK 1
#define ERROR 0
typedef int Status;
typedef int ElemType;
//顺序表存储结构
typedef struct
{
ElemType data[MAXSIZE];
int length;
}SqList;
// 顺序表的初始化
Status InitSqList(SqList* L)
{
L->length = 5;
return OK;
}
//插入
Status InsertElem(SqList* L, int i, ElemType e)
{
int k;
//不考虑扩容,如果线性表满了,就没必要插入了
if (L->length == MAXSIZE)
return ERROR;
if (i<1 || i>L->length)
return ERROR;
//若插入数据不在表尾
if (i <= L->length)
{
//将要插入位置后数据元素向后移动一位
for (k = L->length - 1; k >= i - 1; k--)
L->data[k + 1] = L->data[k];
L->data[i - 1] = e;
}
L->length++;
return OK;
}
//删除
Status DeleteElem(SqList* L, int i, ElemType* e)
{
int k;
//表为空,查找不在表范围,返回错误
if (L->length == 0 || i<1 || i>L->length)
return ERROR;
*e = L->data[i - 1];
//如果删除元素不在表尾
if (i < L->length)
{
for (k = i; k < L->length; k++)
L->data[k - 1] = L->data[k];
}
L->length--;//记录表长的变量-1
return OK;
}
// 顺序表的查找
int GetElemType(SqList L, ElemType e)
{
for (int i = 0; i < L.length; i++)
{
if (L.data[i] == e)
{
return i + 1;
}
}
return 0; // 查找失败
}
//打印顺序表
Status PrintSqList(SqList L,char* s)
{
int i = 0;
printf("%s", s);
for (i = 0; i < L.length; i++)
printf("%d ", L.data[i]);
return OK;
}
int main()
{
//创建并初始化顺序表
SqList L;
ElemType e;
int i = 0;
InitSqList(&L);
printf("请输入5个整数录入线性表:");
for (i = 0; i < 5; i++)
scanf("%d", &L.data[i]);
PrintSqList(L, (char*)"初始化成功,线性表存储数据为:");
//插入
printf("\n请输入要插入的位置和整数:");
scanf("%d%d", &i, &e);
InsertElem(&L, i, e);
PrintSqList(L, (char*)"插入后的线性表为:");
//查询
printf("\n请输入要查询的数字:");
scanf("%d", &e);
int ret=GetElemType(L, e);
if (ret == 0)
printf("未找到!");
else
printf("找到了,%d在第%d个位置上!", e,ret);
//删除
printf("\n请输入删除的位置:");
scanf("%d", &i);
DeleteElem(&L, i, &e);
PrintSqList(L, (char*)"删除后的线性表为:");
return 0;
}
下面我们来讨论遗留的问题:
SqList* L和SqList L什么场景,需要哪种选择?(看看上面的完整调用,会理解更深)
答:根据需求。先说背景,使用C语言来实现上述的用函数封装起来的算法,我们要事先知道形参是实参的一份临时拷贝,我们在函数体内对参数的操作,出了函数就不起作用了,这时C语言的指针就起到了作用,我们就传这个变量的地址,函数部分的形参就用指针接收
例如:初始化InitSqList(&L);对应函数Status InitSqList(SqList* L) ;我们能得到什么信息呢?
结构体变量L的地址作为参数传给函数InitSqList
我们要在这个函数中对结构体变量的本体做点什么😁
这个函数的形参和实参名字是一样的
例如:查找GetElemType(L, e);对应函数int GetElemType(SqList L, ElemType e);我们能得到什么信息?
结构体变量L作为参数传给函数 GetElemType
我们不需要在这个函数中对变量的本体进行修改——毕竟只进行查找嘛😆
这和函数的形参和实参名字是一样的
写在最后
顺序表以及即将要学习的链表,在后面很重要,是后面的基础,同时顺序表和数组紧密相关,只要弄清楚结构,有C语言基础,就可以过关了
👍🏻 点赞,你的认可是我创作的动力!
⭐ 收藏,你的青睐是我努力的方向!
✏️ 评论,你的意见是我进步的财富!