顺序表是C语言中一种常用的线性数据结构,用于存储相同类型的元素。它通过一段连续的内存空间来顺序存储元素,同时利用一个整数变量记录当前元素的个数。在C语言中,顺序表通常使用数组来实现。数组是一段连续的内存空间,可以按照索引位置访问其中的元素。在顺序表中,数组的长度固定,即在创建顺序表时需要指定最大存储容量。顺序表的基本操作包括插入、删除、查找和遍历操作,顺序表提供了一种简单直观的线性数据结构,适用于对元素访问频繁、随机访问较多的场景。在C语言中,通过数组实现顺序表是一种常见的方式。
文末有完整代码
本例中将main.c 与 .h 文件两者分开写.c文件模拟用户操作界面(选择要实现的功能).h模拟底层代码
(如过不能理解的读者可查看文章末端将会有完整代码展示)
.c文件中只调用自定义的函数;
.h文件中存放自定义的函数;
为避免每个功能部分展示的混乱,本文采取: <1>.c文件代码编写展示
<2>.h文件的定义
<3>输出结果展示
的形式进行阐述
引用自定义头文件用 "" ,此处区别于
#include"module.h"//在.c文件中调用自定义的头文件module.h
因为我用的是VS,所以案例代码输入函数scanf_s与标准scanf存在一点差别,差别如下:
scanf()不会检查输入边界,可能造成数据溢出。
scanf_s()会进行边界检查。
scanf()函数是标准C中提供的标准输入函数,用以用户输入数据
scanf_s()函数是Microsoft公司VS开发工具提供的一个功能相同的安全标准输入函数,从vc++2005开始,VS系统提供了scanf_s()。在调用该函数时,必须提供一个数字以表明最多读取多少位字符。
如果只写
scanf_s("%d", &L.elem[i].number);
VS将会提示
warning C4473: “scanf_s”: 没有为格式字符串传递足够的参数
在后面添加对输入大小定义的函数sizeo可以解决此类问题
scanf_s("%d", &L.elem[i].number,sizeof(L.elem[i].number));
#include
#include
#include"module.h"
对功能面板顺序表初始化的定义:
#include
#include
#include"module.h"
int main()
{
int Return = 0, xuhao = 0, weizhi = 0, N = 0, Choice;
int n, select, search;
BOOK reg = { 0,"abc",0.0 };//给case1中的函数传参用,VS必须要初始化,好烦!!
//初始化顺序表
SqList L;//将L定义为SqList类型的变量
printf("--------------------------------------------------\n");
printf(" 顺序表初始化 \n");
printf("--------------------------------------------------\n");
while (1)
{
printf("请输入要选择的功能 1:插入 2:删除数据 3:查找 4:返回第i个数的值 5:删除位置 6:退出\n");
scanf_s("%d", &Choice, sizeof(Choice));
switch (Choice)
{
case 1:
printf("--------------------------------------------------\n");
printf(" 顺序表插入 \n");
printf("--------------------------------------------------\n");
break;
case 2:
printf("--------------------------------------------------\n");
printf(" 顺序表删除 \n");
printf("--------------------------------------------------\n");
break;
case 3:
printf("--------------------------------------------------\n");
printf(" 顺序表查找 \n");
printf("--------------------------------------------------\n");
break;
case 4:
printf("--------------------------------------------------\n");
printf(" 顺序表返回第i个数的值 \n");
printf("--------------------------------------------------\n");
break;
case 5:
printf("--------------------------------------------------\n");
printf(" 顺序表删除位置 \n");
printf("--------------------------------------------------\n");
break;
case 6:exit(0);
default:printf("非法输入!!"); break;
}
}
}
#include
#include
#include
#define MAXSIZE 20//限制结构体BOOK最大容量
#define QUANTITY 21//限制顺序表最大元素个数
.h文件对后续函数变量的定义、顺序表的定义
typedef struct//图书基本信息定义
{
int number;//书号
char name[20];//书名
float price;//书价
}BOOK;//数据域的结构体类型
typedef struct
{
BOOK* elem;//存储空间的基地址,顺序表指针动态赋存储空间
//L.elem存储所需空间的地址,后将为基地址申请空间存放数据
int length;//图书表中当前图书个数
}SqList;//图书表的顺序存储结构类型为SqList
SqList L;//将L定义为SqList类型的变量
//顺序表的初始化
int InitList(SqList& L, int N)//传引用,以及传参N
{
L.elem = new BOOK[MAXSIZE];//为顺序表分配一个大小为MAXSIZE的数组空间
L.length = N;//定义顺序表表长为N
if (L.elem == NULL)//判断申请空间是否成功,如果申请空间不成功返回false
{
return false;//将函数返回值定义为int
} //return false相当于返回0,return true相当于返回1
else//成功申请空间
{ //继续对传入参数N(表长也是元素个数)进行判断
if (L.length == 0)//L.length = N=0
{
printf("您所选择的顺序表元素个数为:0");
}
else if (L.length < 0)//L.length=N=0
{
printf("非法输入请重新输入:");
return false;
}
else if (L.length > QUANTITY)//如果超出顺序表最大容量
{
printf("对不起您输入的数据超出存储范围,请重新输入:");
return false;
}
else//合法输入1<=N<= QUANTITY
{//初始化顺序表
for (int i = 0; i < L.length; i++)
{
printf("请输入第%d本书的书号、书名、单价:", i + 1);
scanf_s("%d", &L.elem[i].number, sizeof(L.elem[i].number));
scanf_s("%s", &L.elem[i].name, sizeof(L.elem[i].name));
scanf_s("%f", &L.elem[i].price, sizeof(L.elem[i].price));
}
printf("输入完成,您定义的顺序表为:\n");//打印顺序表内容
for (int i = 0; i < L.length; i++)
{
printf("%d\t%s\t%.2f\n", L.elem[i].number, L.elem[i].name, L.elem[i].price);
}
return true;
}
}
}
1:N=22超出范围, .h文件中定义最大为21个元素
#define QUANTITY 21
2:非法输入N=-1重新输入N=2
case 1:
printf("--------------------------------------------------\n");
printf(" 顺序表插入 \n");
printf("请输入您想插入的位置:");
scanf_s("%d", &n);//插入的位置为n-1
Return = ListInsert(L, n, reg);//Return承接函数InitList(L,N)的返回值
while (!Return)//如果Return不为1,没有正确执行时(N没有正确输入)重新执行函数
{
scanf_s("%d", &n);
ListInsert(L, n, reg);
}
printf("--------------------------------------------------\n");
break;
因为怕弄得好乱就直接在函数前对BOOK reg;中结构体reg进行了定义和初始化,在其他编译器中不对reg初始化也可以运行成功;
BOOK reg = { 0,"abc",0.0 };//对reg进行定义,寄存想要插入的元素
int ListInsert(SqList &L, int n, BOOK reg)//插入,在表的n位插入一个数据位e元素
{
if ((n >= 1) && (n < L.length))
{
printf("\n请输入要插入的元素:\n");
scanf_s("%d", ®.number, sizeof(reg.number));
scanf_s("%s", ®.name, sizeof(reg.name));
scanf_s("%f", ®.price, sizeof(reg.price));
for (int i = L.length; i >= n; i--)
{
L.elem[i] = L.elem[i - 1];//将表第n位及以后的所有位往后移动1位
}
L.elem[n - 1] = reg;//将结构体reg的数据赋给L.elem[n - 1]
L.length = L.length + 1;//链表长度+1
printf("插入成功,表长为%d\n", L.length);
for (int i = 0; i < L.length; i++)
{
printf("number[%d]==%d\t", i, L.elem[i].number);
printf("name[%d]==%s\t", i, L.elem[i].name);
printf("price[%d]==%2.f\t", i, L.elem[i].price);
printf("\n");
}
return true;
}
else//输入数据不合法时,提示输入错误,并返回0
{
printf("插入位置错误,请重新输入:\n");
return false;
}
}
1:第一次输入为7,第二次输入为-1,超出范围,进行数据输入合法性判断,不合法可重新输入
2:成功输入后,输入插入元素的成员数据,并打印顺序表
3:连续插入并打印顺序表
case 2:
printf("--------------------------------------------------\n");
printf(" 顺序表删除 \n");
printf("请选择检索方式用以删除该书信息:1:书号 2:书名 \n");
scanf_s("%d", &select);
Return = DeleteElement(L, select);
while (!Return)
{
scanf_s("%d", &select);
DeleteElement(L, select);
}
printf("--------------------------------------------------\n");
break;
int DeleteElement(SqList& L, int n)//删除顺序表某个元素 (位置未知)
{
int i, j;
int shuhao;
char shuming[20];
if ((n >= 1) && (n <= L.length))//对输入合法性进行判断
{
switch (n)
{
case 1:
printf("请输入您想删除书的书号:");
scanf_s("%d", &shuhao);
for (i = 0; i < L.length; i++)
{
if (L.elem[i].number == shuhao)//判断输入书号是否存在
{
for (j = i; j < L.length - 1; j++)
{
L.elem[j] = L.elem[j + 1]; // 将后面的元素往前移动
}
L.length--; // 线性表长度减1
printf("删除成功,表长为%d\n", L.length);
for (int i = 0; i < L.length; i++)
{
printf("number[%d]==%d\t", i, L.elem[i].number);
printf("name[%d]==%s\t", i, L.elem[i].name);
printf("price[%d]==%2.f\t", i, L.elem[i].price);
printf("\n");
}
return true;
break;
}
else
{
printf("删除失败,未检索到该书,请重新选择删除方式\n");
return false;
}
}
break;
case 2:
printf("请输入您想删除书的书名:");
scanf_s("%s", &shuming, sizeof(shuming));
for (i = 0; i < L.length; i++)
{
if (strcmp(L.elem[i].name, shuming) == 0)
{
for (j = i; j < L.length - 1; j++)
{
L.elem[j] = L.elem[j + 1]; // 将后面的元素往前移动
}
L.length--; // 线性表长度减1
printf("删除成功,表长为%d\n", L.length);
for (int i = 0; i < L.length; i++)
{
printf("number[%d]==%d\t", i, L.elem[i].number);
printf("name[%d]==%s\t", i, L.elem[i].name);
printf("price[%d]==%2.f\t", i, L.elem[i].price);
printf("\n");
}
return true;
break;
}
else
{
printf("删除失败,未检索到该书,请重新选择删除方式\n");
return false;
}
}
break;
}
}
else
{
printf("输入非法,请重新输入:");
return false;
}
}
下面这部分功能将对输入书名和顺序表所存书名是否匹配进行判断,要用到strcmp函数,其实质使对字符串每一位字符对应的ASCII码进行比较
int strcmp(const char* str1, const char* str2)
头文件:#include
返回值:str1=str2 则返回0;str1>str2 则返回大于0的值;str1 3:按书号删除,未检索到书号 4:成功输入,删除后打印顺序表 5:按书名成功删除 知识点:\t为间隔一个制表位 \n 换行 \t\n先间隔一个制表位后换行 1:非法输入以及有效序号输入运行结果展示 两次非法位置输入以及有效输入运行结果展示 二次修改后加强了对输入边界的限定,同时优化了代码对于非法输入时的重复输入功能的实现 <3>运行结果展示
1:选择功能,非法输入
2:选择删除方式,非法输入
6:按书名未检索到,运行结果展示
功能3:顺序表查找
<1>.c文件对函数的引用
case 3:
printf("--------------------------------------------------\n");
printf(" 顺序表查找 \n");
printf("请选择检索方式用以查找该书信息:1:书号 2:书名 \n");
scanf_s("%d", &search);
Return = LocateElem(L, search);
while (!Return)
{
scanf_s("%d", &search);
LocateElem(L, search);
}
printf("--------------------------------------------------\n");
break;
<2>.h文件对函数以及相关变量的定义
int LocateElem(SqList& L, int search)//查找
{
int shuhao;
char shuming[20];
if ((search >= 1) && (search <= 2))//对输入合法性进行判断
{
switch (search)
{
case 1:
printf("请输入您想查找书的书号:");
scanf_s("%d", &shuhao, sizeof(shuhao));
for (int i = 0; i < L.length; i++)
{
if (L.elem[i].number == shuhao)
{
printf("该值位于第%d位\n", i + 1);
return true;
}
else if ((i == L.length-1)&&(L.elem[i].number !=shuhao))
{
printf("未检索到该书号,请重新输入:\n");
return false;
}
}
case 2:
printf("请输入您想查找书的书名:");
scanf_s("%s", &shuming, sizeof(shuming));
for (int i = 0; i < L.length; i++)
{
if (strcmp(L.elem[i].name, shuming) == 0)
{
printf("该值位于第%d位\n", i + 1);
return true;
break;
}
else if ((i == L.length-1) && (L.elem[i].name != shuming))
{
printf("未检索到该书名,请重新输入:\n");
return false;
}
}
}
}
else
{
printf("输入非法请重新输入:\n");
return false;
}
}
<3>运行效果展示
1:非法输入
2:按书号查询,成功查询;按书号查询,超出范围的书号,未检索到提示
2:按书名查询,成功查询;按书号查询,超出范围的书号,未检索到提示
功能4:显示第i个元素的信息
<1>.c文件对函数的引用
case 4:
printf("--------------------------------------------------\n");
printf(" 顺序表返回第i个数的值 \n");
printf("请问您想查询的本书所在的序号: \n");
scanf_s("%d", &xuhao);
Return = LocateElem(L,xuhao);
if (!Return)
{
scanf_s("%d", &search);
Get(L, search);
}
printf("--------------------------------------------------\n");
break;
<2>.h文件对函数以及相关变量的定义
int Get(SqList &L, int f)//取值,返回第i个位置的元素
{
if (f < 0 || f > L.length)//合法性判断
{
printf("查找位序不存在");
return false;
}
else
{
printf("number[%d]==%d\t", f - 1, L.elem[f - 1].number);
printf("name[%d]==%s\t", f - 1, L.elem[f - 1].name);
printf("price[%d]==%2.f\n", f - 1, L.elem[f - 1].price);
return true;
}
}
<3>运行效果展示
功能5: 顺序表删除位置
<1>.c文件对函数的引用
case 5:
printf("--------------------------------------------------\n");
printf(" 顺序表删除位置 \n");
printf("请问您想删除的位置: \n");
scanf_s("%d", &weizhi);
Return = ListDelete(L, weizhi);
if (!Return)
{
scanf_s("%d", &weizhi);
ListDelete(L, weizhi);
}
printf("--------------------------------------------------\n");
break;
<2>.h文件对函数以及相关变量的定义
int ListDelete(SqList& L, int n)//删除相应位置的数
{
if (n<1 || n>L.length)//输入数据不合法时,显示输入错误
{
printf("删除位置错误,请重新输入\n");
return false;
}
else
{
for (int i = n; i < L.length; i++)
{
L.elem[i - 1] = L.elem[i];//将需要删除的位置的后一个位置的数据赋给前一个位置
}//类似于前移,但是在移动时会覆盖掉前面的数据
L.length--;//链表长度减一
for (int i = 0; i < L.length; i++)
{
printf("number[%d]==%d\t", i, L.elem[i].number);
printf("name[%d]==%s\t", i, L.elem[i].name);
printf("price[%d]==%2.f\t", i, L.elem[i].price);
printf("\n");
}
return true;
}
}
<3>运行效果展示
功能六:退出
四:完整代码展示
<1>.c文件
#include
<2>.h文件
#include