代码如下:
PS:代码段的解释都用注释形式给出
#include<iostream>
using namespace std;
#define MaxSize 20 // 宏定义 相当于将文中所有 MaxSize 全部替换成 20 ,MaxSize相当于一个占位符
typedef int DataType; //定义int类型的别名 DataType
typedef struct List // 这边设置了结构体 List 的别名为 SeqList
{
DataType data[MaxSize]; // 以int类型的别名(实际上就是int型) 定义了一个数组,容量为MaxSize(实际上是20 个int类型的空间)
int length; // 在结构体中还定义了表长,方便描述顺序表
}SeqList;
void InitList(SeqList *L) // 这边是定义顺序表的函数定义,一般只需将length置为空,证明是空表即可,因为数组空间是可复写的
{ // 即使原数组空间中 data[0] data[1]……还存有数据,但是在插入的时候是直接以 length 为参考覆盖写入的
L->length=0;
}
int Length_List(SeqList *L) // 定义返回表的长度的函数
{
return L->length;
}
DataType InsertList(SeqList *L, int &i, DataType &e) // 定义插入元素的函数,参数中的 int &i 和 DataType &e 为C++中的引用,可以自行了解下,主要作用为直接调用原变量节省栈空间
{
int j;
if(L->length == MaxSize) // 这里可以看出,插入时是以 length 为参考,并不涉及原数组data[0]等实际存储内容,是默认覆盖的
{cout<<"表满,无法插入。"<<endl; // 这边判断了表示表厂的 length 是否等于分配的最大空间 【这边数组分配的空间是固定的】 当然也可以用动态分配 比如 C++ 中的
return 0; // malloc 和 new 可以从堆中分配空间,就不用局限于 MaxSize 的大小,只受到堆空间的限制
}
if(i<1||i>L->length+1)
{
cout<<"插入位置错误,请重新输入。"<<endl; // 判断插入元素的位置 i 是否在范围内
return 0;
}
for(j=L->length-1; j>=i-1;j--) // 如果表未满且插入位置正确,那么从表尾元素【即 data[length-1] 开始,依次向 data[i+1] 赋值】 这样相当于将 data[i] 的值赋值
{ // 给了 data[i+1], 即从要插入的 i 位置开始,元素都向后移动一位,为要插入进来的元素腾出空间覆盖写入。
L->data[j+1] = L->data[j];
}
L->data[i-1] = e;
L->length++; // 别忘了插入之后要将 length++ 这是进行表操作的重要指标
cout<<"\n";
return 1;
}
int Search_index_List(SeqList *L, int &i) // 按序号i查找
{
if(i<1||i>L->length) // 这边是一样的判断 不再赘述
{
cout<<"超出查询范围,请重新输入。"<<endl;
return 0;
}
else
{
cout<<"\n查询的第"<<i<<"个位置的值为: "<<L->data[i-1]<<"\n"<<endl; // 想着人性化一点,没有只返回 i 对应的值
return 0;
}
}
int Search_value_List(SeqList *L, DataType &e) // 按值 e 查找
{
int j;
while(j<=L->length && L->data[j-1] != e) // “ && ” 符号为 C++中的逻辑判断符,即两边的两个条件 有一个为假,那么整条语句都为假==>【有假为假,全真则真】,对应的是 “|| ”符号,含义为【有真则真,全假为假】
j++; // 判断当 计数变量 j 小于等于 length 并且 data[j-1] 的值不与要查找的 e 相等时,计数变量 j 自增 1 , 继续向下查找
if(L->data[j-1] == e) // 如果跳出 while 循环,那么我们第一步判断是否是在表中找到了我们想要的 e , 如果是,则输出对应语句
{
cout<<"值为"<<e<<"的元素在第"<<j<<"个位置。\n"<<endl;
return 0;
}
else
{
cout<<"未能查询到值为"<<e<<"的元素,请检查后重新输入。"<<endl; // 如果没有查找到,即 计数变量 j 已经超出表长 length 的范围了,那么就是在表中没有找到
return 0;
}
}
DataType Delete_index_List(SeqList *L, int i) // 按序号删除
{
int j;
if(L->length == 0) // 一样的判断,不再赘述
{
cout<<"表为空,无元素可删除。"<<endl;
return 0;
}
if(i<1||i>L->length)
{
cout<<"序号超出范围,请重新输入。";
return 0;
}
cout<<"序号为 "<<i<<"的元素: "<<L->data[i-1] <<"已删除"<<endl; // 这边是因为我想输出被删除的元素的值,如果在 for 循环删除语句之后输出,那么对应的 data[i-1] 就是被删除后表中的第一个元素了
// 就是被删除后表中的第一个元素了,显然不符合我们的想法
for(j=i-1; j<=L->length-1;j++) // for 循环 将计数变量 j = [i-1] 即序号为 i 的元素在数组中的下标。 PS:数组中的下标实际意义为首地址的偏移量,首地址为第一个元素
{ // 所以第一个元素的偏移量为 0 ,即第一个元素的下标为 data[0]。
L->data[j] = L->data[j+1]; // 从序号为 j 的元素开始,将 data[j+1] 的元素赋值给 data[j] ,即从后向前依次覆盖前一个元素,是为表中的删除操作
}
L->length--; // 同样的,删除完别忘记将 length 减 1
return 1;
}
DataType Delete_value_List(SeqList *L, DataType &e) // 按值删除
{
int i=0;
if(L->length == 0)
{
cout<<"表为空,无元素可删除。"<<endl; // 同样的判断,不再赘述
return 0;
}
while(i<L->length && L->data[i] != e) // 和按值查找是一样的操作
{
i++;
}
if(L->data[i] == e) // 找到值为 e 的下标 i 后,进行按序号删除同样的步骤
{
cout<<"表中序号为 "<<i<<"的元素: "<<e<<"已删除"<<endl;
for(;i<=L->length-1;i++)
{
L->data[i] = L->data[i+1];
}
L->length--;
}
else
{
cout<<"表中无元素为"<<e<<"的值,请检查后重新输入。"<<endl;
return 0;
}
}
void Print_List(SeqList *L) // 输出顺序表
{
if(L->length == 0)
{
cout<<"表中无元素可输出。\n";
}
else
{
int i;
cout<<"表中元素为:" ;
for(i=0; i<L->length; i++) // 这边用了 for 循环来输出表中【表示为一维数组】 的元素
{
cout<<L->data[i]<<" ";
}
cout<<"\n"<<endl;
}
}
int main()
{
SeqList L; // 要先定义一个 SeqList 类型的变量 L
int input; // 这边是输入的选项接受变量
while(1) // 用 while 永真循环 这样不会初始化完顺序表就退出哈哈哈
{
cout<<"********************\n";
cout<<"菜单选项:"<<"\n"
<<"1 初始化顺序表"<<"\n"
<<"2 查看顺序表的长度"<<"\n"
<<"3 插入元素"<<"\n"
<<"4 查找元素"<<"\n"
<<"5 删除元素"<<"\n"
<<"6 输出顺序表"<<"\n"
<<"0 退出"<<"\n\n"<<endl;
cout<<"请输入选项: ";
cin>>input; // 这边接受输入的选项 【PS:本来还想用 cin.fail() 控制下错误输出,即我定义的是 int 型的变量 input 如果有人输入了字符型,该给他什么样的反馈
// 也要充分注意到程序的健壮性,但是好不容易改错调试搞了半天就懒得弄啦哈哈哈哈 有兴趣的可以试一下】
switch(input) // switch 根据 input 的值执行相应的 case,记得每个 case 的最后一定要加 break 不然会从当前 case 开始依次向下执行,直到遇到 break
{
int i,sw_input; // 由于有多级选项 所以定义了 i 和 switch 内的 input
DataType e; // 这边是定义了值 e
case 1:
InitList(&L); // 这边要注意了,因为在函数定义时,参数列表给的类型是 SeqList *L 即指针类型,指针变量是地址,所以这边要取 L 的地址作为参数传入
cout<<"初始化成功\n"<<endl;
break;
case 2:
cout<<"顺序表的长度为: "<<Length_List(&L)<<"\n"<<endl; // 为什么函数定义要用指针这么麻烦呢,因为函数内的结果如果想带出来要么通过返回值 要么通过传址,可是返回值
break; // 还要对应变量接收 比较麻烦,具体想了解的可以去查阅下“函数的返回值,传址、引用啥的巴拉巴拉的 网上好多
case 3: // 大佬的解答都很详细
cout<<"请输入插入的位置及元素: ";
cin>>i>>e;
if(cin.fail()) break; // 这边就是我上面提到的 cin 的异常捕捉
InsertList(&L, i, e);
break;
case 4:
cout<<"请选择:\n"
<<"1 按序号查找"<<"\n"
<<"2 按值查找\n"<<endl;
cout<<"请输入选项:";
cin>>sw_input;
if(cin.fail()) break;
switch(sw_input) // 多级选项再用一次 switch 语句
{
case 1:
cout<<"请输入要查找的序号: ";
cin>>i;
if(cin.fail()) break;
Search_index_List(&L, i);
break;
case 2:
cout<<"请输入要查找的值: ";
cin>>e;
if(cin.fail()) break;
Search_value_List(&L, e);
break;
}
break;
case 5:
cout<<"请选择:\n"
<<"1 按序号删除"<<"\n"
<<"2 按值删除\n"<<endl;
cout<<"请输入选项:";
cin>>sw_input;
if(cin.fail()) break;
switch(sw_input)
{
case 1:
cout<<"请输入要删除的序号: ";
cin>>i;
if(cin.fail()) break;
Delete_index_List(&L, i);
break;
case 2:
cout<<"请输入要删除的值: ";
cin>>e;
if(cin.fail()) break;
Delete_value_List(&L, e);
break;
}
break;
case 6:
Print_List(&L);
break;
case 0:
return 0;
default: cout<<"ERROR!"<<endl; // default 是 switch 里面默认的错误捕捉,即如果 case 都对应不上,就执行 default
}
}
}
第一篇CSDN博客
写于2019年12月4日14:58