本文将通过完成以下内容来展示在数据结构中线性表的一些基本操作,代码解释标注全面而且清晰,代码书写也按照规范,适合初学者进行学习,前期的细节准备能够为后期建立属于自己的数据结构库打下坚实基础,本篇文章算是本人的一些学习记录分享,希望对有需要的小伙伴提供一些帮助~
定义一个包含学生信息(学号,姓名,成绩)的顺序表和链表,使其具有如下功能:
(1) 输入学生信息;
(2) 逐个显示学生表中所有学生的相关信息;
(3) 根据姓名进行查找,返回此学生的学号和成绩;
(4) 根据指定的位置可返回相应的学生信息(学号,姓名,成绩);
(5) 给定一个学生信息,插入到表中指定的位置;
(6) 删除指定位置的学生记录;
(7) 统计表中学生个数。
一、顺序表:
顺序表
1、源程序及主要算法说明
#include<iostream>
#include<fstream>
#include<string>
#include<iomanip>
using namespace std;
#define OK 1
#define ERROR 0
#define OVERFLOW -2
typedef int Status; //Status 是函数返回值类型,其值是函数结果状态代码。
#define MAXSIZE 100 //顺序表可能达到的最大长度
struct Student {
string no;//学号
string name;//姓名
double grade;//成绩
};
typedef Student ElemType;
//------顺序表的存储结构通用格式------
typedef struct {
ElemType *elem; //存储空间的基地址
int length; //当前长度
} SqList;
Status InitList_Sq(SqList &L) { //算法2.1 顺序表的初始化
//构造一个空的顺序表L
L.elem = new ElemType[MAXSIZE]; //为顺序表分配一个大小为MAXSIZE的数组空间
if (!L.elem)
exit(OVERFLOW); //存储分配失败退出
L.length = 0; //空表长度为0
return OK;
}
Status GetElem(SqList L, int i, Student &e) {//算法2.2 顺序表的取值
if (i < 1 || i > L.length)
return ERROR; //判断i值是否合理,若不合理,返回ERROR
e = L.elem[i - 1]; //elem[i-1]单元存储第i个数据元素
return OK;
}
int LocateElem_Sq(SqList L, string e) { //算法2.3 顺序表的查找
for (int i = 0; i < L.length; i++)
if (L.elem[i].name == e)
return i + 1;//查找成功,返回序号i+1
return 0;//查找失败,返回0
}
Status ListInsert_Sq(SqList &L, int i, Student e) { //算法2.4 顺序表的插入
//在顺序表L中第i个位置之前插入新的元素e
//i值的合法范围是1<=i<=L.length+1
if ((i < 1) || (i > L.length + 1))
return ERROR; //i值不合法
if (L.length == MAXSIZE)
return ERROR; //当前存储空间已满
for (int j = L.length - 1; j >= i - 1; j--)
L.elem[j + 1] = L.elem[j]; //插入位置及之后的元素后移
L.elem[i - 1] = e; //将新元素e放入第i个位置
++L.length; //表长增1
return OK;
}
Status ListDelete_Sq(SqList &L, int i) { //算法2.5 顺序表的删除
//i值的合法范围是1<=i<=L.length
if ((i < 1) || (i > L.length))
return ERROR; //i值不合法
for (int j = i; j <= L.length; j++)
L.elem[j - 1] = L.elem[j]; //被删除元素之后的元素前移
--L.length; //表长减1
return OK;
}
Status ListLength_Sq(SqList L){
return L.length;
}
int main() {
SqList L;
int i = 0, temp, a, c, choose;
int grade;
int count;
string name;
Student e;
string head_1, head_2, head_3;
cout << "1. 建立\n";
cout << "2. 输入\n";
cout << "3. 取值\n";
cout << "4. 查找\n";
cout << "5. 插入\n";
cout << "6. 删除\n";
cout << "7. 输出\n";
cout << "8. 获取数据个数\n";
cout << "0. 退出\n\n";
choose = -1;
while (choose != 0) {
cout << "请选择:";
cin >> choose;
switch (choose) {
case 1://创建顺序表
if (InitList_Sq(L))
cout << "成功建立顺序表\n\n";
else
cout << "顺序表建立失败\n\n";
break;
case 2: {//顺序表信息输入
i = 0;
L.elem = new Student[MAXSIZE];
if (!L.elem)
exit(OVERFLOW);
L.length = 0;
fstream file;
file.open("Student.txt");
if (!file) {
cout << "错误!未找到文件!" << endl;
exit(ERROR);
}
file >> head_1 >> head_2 >> head_3;
while (!file.eof()) {
file >> L.elem[i].no >> L.elem[i].name >> L.elem[i].grade;
i++;
}
cout << "输入 Student.txt 信息完毕\n\n";
L.length = i;
file.close();
}
break;
case 3://顺序表的取值
cout << "请输入一个位置用来取值:\n";
cin >> i;
temp = GetElem(L, i, e);
if (temp != 0) {
cout << "查找成功\n";
cout << "第" << i << "个学生的信息是:\n";
cout << left << setw(15) << e.no << "\t" << left << setw(50)
<< e.name << "\t" << left << setw(5) << e.grade << endl
<< endl;
} else
cout << "查找失败!位置超出范围\n\n";
break;
case 4: //顺序表的查找
cout << "请输入所要查找的学生姓名: \n\n";
cin >> name;
temp = LocateElem_Sq(L, name);
if (temp != 0) {
cout << "查找成功\n";
cout << "该学生对应的学号为:" << L.elem[temp - 1].no;
cout << "\n该学生对应的成绩为:" << L.elem[temp - 1].grade << endl << endl;
} else
cout << "查找失败!没有这个名字对应的学生\n\n";
break;
case 5: //顺序表的插入
cout << "请输入插入的位置和学生信息,包括:学号 姓名 成绩(用空格隔开):";
cin >> a;
cin >> e.no >> e.name >> e.grade;
if (ListInsert_Sq(L, a, e))
cout << "插入成功.\n\n";
else
cout << "插入失败.\n\n";
break;
case 6: //顺序表的删除
cout << "请输入所要删除的学生的位置:";
cin >> c;
if (ListDelete_Sq(L, c))
cout << "删除成功.\n\n";
else
cout << "删除失败.\n\n";
break;
case 7: //顺序表的输出
cout << "当前学生系统信息(顺序表)读出:\n";
for (i = 0; i < L.length; i++)
cout << left << setw(15) << L.elem[i].no << "\t" << left
<< setw(50) << L.elem[i].name << "\t" << left
<< setw(5) << L.elem[i].grade << endl;
cout << endl;
break;
case 8: //顺序表统计学生个数
cout << "当前表中学生个数为:";
temp = ListLength_Sq(L);
cout<<temp;
cout<<endl<<endl;
break;
}
}
return 0;
}
运行结果页面如下所示:具体内容尝试可自行运行操作
二、链表:
链表
1、源程序及主要算法说明
#include<iostream>
#include<string>
#include<string.h>
#include<iomanip>
#include<fstream>
using namespace std;
#define OK 1
#define ERROR 0
typedef int Status;
typedef struct {
char no[8]; //8位学号
char name[20]; //姓名
int grade; //成绩
}Student;
typedef Student ElemType;
typedef struct LNode{
Student data; //结点数据域
struct LNode *next; //结点指针域
}LNode,*LinkList; //LinkList 为指向结构体的 LNode 的指针类
//创建单链表--前插法
bool CreateList_H1(LinkList &L,int n){
L=new LNode;
LNode *p;
int i;
L->next=NULL; //先建立一个带头结点的单链表
for(i=0;i<n;++i){
p=new LNode; //生成新结点
cin>>p->data.no>>p->data.name>>p->data.grade; //输入元素值,需要按结构体类型进行修改
p->next=L->next;
L->next=p; //插入到表头
}
}//CreateList_H1
//显示
void display(LinkList L){
LNode *p = L->next;
while(p){
cout << p->data.no<<p->data.name<<p->data.grade <<endl;
p = p->next;
}
cout<<endl;
}
//以文件读入方式接收数据、头插法创建单链表
void CreateList_H2(LinkList &L, int n) {
string head_1, head_2, head_3;
int length;
//逆位序输入 n 个元素的值,建立到头结点的单链表 L
LNode *p;
L = new LNode;
L->next = NULL; //先建立一个带头结点的空链表
length = 0;
//以下开始文件读写
fstream file;
//第 1 步:打开文件,案例要求文件与代码放在同一目录下,
//而且文件名一定是以下函数的参数如本例是:student.txt
file.open("student.txt");
if (!file) {
cout << "未找到相关文件,无法打开!" << endl;
exit(ERROR);
}
file >> head_1 >> head_2 >> head_3;//读取第一行作为表头(学号、姓名、成绩这些内容)
while (!file.eof()) {
p = new LNode; //生成新结点*p
file >> p->data.no >> p->data.name >> p->data.grade; //输入元素值赋给新结点*p 的数据域
p->next = L->next;
L->next = p; //将新结点*p 插入到头结点之后
length++;//同时对链表长度进行统计
}
file.close();
} //CreateList_H2
Status GetElem(LinkList L, int i, ElemType &e){
//在带头结点的单链表 L 中根据序号 i 获取元素的值,用 e 返回第 i 个数据元素的值
LNode *p = L->next;
int j = 1;
while(p&&j<i){
p = p->next;
++j;
}
if(!p || j > i) return ERROR;
e = p->data;
//cout << e;
return OK;
}
//按姓名进行查找
LNode *LocateElem(LinkList L, string name) {
//在带头结点的单链表 L 中查找值为 e 的元素
//此处可以按某个字段进行查找,比如按成绩、姓名、学号
LNode *p = L->next;
while(p && p->data.name != name) {
p = p->next;
}
return p;
}
//按成绩进行查找
LNode *LocateElem2(LinkList L, int grade) {
//在带头结点的单链表 L 中查找值为 e 的元素
//此处可以按某个字段进行查找,比如按成绩、姓名、学号
LNode *p = L->next;
while(p && p->data.grade != grade) {
p = p->next;
}
return p;
}
//插入
Status ListInsert(LinkList &L, int i, ElemType e){
//在带头结点的单链表 L 中第 i 个位置插入值为 e 的新结点
LNode *p = L;
int j = 0;
while(p && (j < i - 1)){
p = p->next;
++j;
}
if(!p || j > i - 1) return ERROR;
LNode *s = new LNode;
s->data = e;
s->next = p->next;
p->next = s;
return OK;
}
//删除
Status ListDelete(LinkList &L, int i){
//在带头结点的单链表 L 中,删除第 i 个元素
LNode *p = L;
int j = 0;
while((p->next) && (j < i - 1)){
p = p->next;
++j;
}
if(!(p->next)|| (j > i -1)) return ERROR;
LNode *q = p->next;
p->next = q->next;
delete q;
return OK;
}
//-----以下为主函数-----
int main(){
LinkList L;
int n,i,grade,a;
int choose;
string head_1, head_2, head_3;
int length;
Student e;
string name;
LNode *p;
cout << "1. 头插法建立:键盘录入方式\n";
cout << "2. 头插法建立:文件导入方式\n";
cout << "3. 按位置取值\n";
cout << "4. 按姓名查找\n";
cout << "5. 按成绩查找\n";
cout << "6. 插入\n";
cout << "7. 删除\n";
cout << "8. 输出\n";
cout << "0. 退出\n\n";
choose = -1;
while (choose != 0) {
cout << "请选择:";
cin >> choose;
switch (choose) {
case 1:
//头插法建立:键盘录入方式
cout <<endl<< "=====头插法创建单链表 1:用键盘输入====="<<endl;
cout<<"请输入单链表的元素个数 n:";
cin>>n;
//测试用键盘输入、头插法创建单链表
if (CreateList_H1(L,n))
cout << "键盘输入方式建立链表成功!\n\n";
cout <<endl<< "=====单链表输出====="<<endl;
//输出单链表
display(L) ;
break;
case 2:
//头插法建立:文件导入方式
//测试以文件读入方式接收数据、头插法创建单链表
cout <<endl<< "=====头插法创建单链表 2:用文件读写====="<<endl;
CreateList_H2(L,length);
//输出单链表
cout <<endl<< "=====单链表输出====="<<endl;
display(L) ;
break;
case 3:
//按位置取值
//单链表的取值
cout <<endl<< "=====单链表的取值====="<<endl;
cout<<"请输入一个位置用来取值:";
cin>>i;
if (GetElem(L,i,e)) { //在 L 中取位置为 i 的值
cout << "查找成功\n";
cout << "第" << i << "位同学的信息是:" ;
cout << e.no<<"\t"<<e.name<<"\t"<<e.grade <<endl<<endl;
}
else
cout << "查找失败\n\n";
break;
case 4:
//按姓名查找
cout <<endl<< "=====按姓名查找====="<<endl;
cout<<"请输入要查找的姓名:";
cin>>name;
//查找姓名为 name 的记录
p=LocateElem(L, name);
if(p!=NULL){
cout << endl<<"查找成功!"<<endl;
cout << p->data.no <<"\t" <<p->data.name <<"\t"<<p->data.grade <<endl<<endl;
}
else
cout << endl<<"不存在姓名为:"<<name<<"的记录!"<<endl<<endl;
break;
case 5:
//按成绩查找
cout <<endl<< "=====按成绩查找====="<<endl;
cout<<"请输入要查找的成绩:";
cin>>grade;
p=LocateElem2(L, grade);
if(p!=NULL){
cout << endl<<"查找成功!"<<endl;
cout << p->data.no <<"\t" <<p->data.name <<"\t"<<p->data.grade <<endl<<endl;
}
else
cout << endl<<"不存在成绩为:"<<grade<<"的记录!"<<endl<<endl;
break;
case 6:
//插入
cout <<endl<< "=====单链表的插入====="<<endl;
cout << "请输入插入的位置和学生的信息,包括:学号 姓名 成绩(请在每次输入一个数值后按回车符):";
cin >> a;
cin >> e.no >> e.name >> e.grade;
if(ListInsert(L, a, e))
cout << "插入成功.\n\n";
else
cout << "插入失败!\n\n";
break;
case 7:
//删除
cout <<endl<< "=====单链表的删除====="<<endl;
cout << "请输入想要删除的位置:";
cin >> i;
if(ListDelete(L,i))
cout << "删除成功!\n\n";
else
cout << "删除失败!\n\n";
break;
case 8:
//显示
cout <<endl<< "=====单链表输出====="<<endl;
//输出单链表
display(L);
break;
return 0;
}
}
}
页面运行结果:
三、结论与体会:
1.顺序表的存储空间必须预先分配,元素个数有一定限制,已造成存储空间浪费或空间溢出现象;而链表不需要为其预先分配空间,只要内存空间允许,链表中元素的个数就没有限制。所以当线性表的长度变化比较大,难以预估存储规模时,宜采用链表作为存储结构。
2.对于链表,在确定插入或删除的位置后,插入或删除操作无需移动数据,只需要修改指针。而对于顺序表,进行插入或删除时,平均要移动表中近一半的节点,所以对于频繁进行插入或删除操作的线性表,宜采用链表作为存储结构。
以上即是本文的全部内容,喜欢可以点赞收藏~