1.顺序表
实验1-顺序表
实验目的:
- 掌握线性表的定义;
- 掌握线性表的顺序存储。
- 掌握线性表的基本操作,如建立、查找、插入和删除等。
实验内容:
定义一个包含学生信息(学号,姓名,成绩)的链表,使其具有如下功能(参见教材中基本操作):
- 根据指定学生个数,逐个输入学生信息(构建表或产生表);
- 逐个显示学生表中所有学生的相关信息(显示表,即遍历表);
- 根据姓名进行查找,返回此学生的学号和成绩(查找);
- 根据指定的位置可返回相应的学生信息(学号,姓名,成绩)(取值 );
- 给定一个学生信息,插入到表中指定的位置(插入);
- 删除指定位置的学生记录(删除);
- 统计表中学生个数(相当于求表长)。
说明
实验指导中的代码在语法上有一点问题,已经做出修改并给出注释:
- 不建议使用
using namespace std;
它可能导入许多你不知道的东西, 可能会和你的变量名冲突; VisualStudio可能出现cout 不明确
的神奇Bug - 使用匿名结构体,
struct Name {} varname;
指导书中代码使用匿名结构体变量
作结构体名
使用,在语义上不合适(教材也这样写) - 函数声明放在 main() 函数里面, 这个应该拿到 main() 前面
代码参考
文件名: SqList.cpp
#include <iostream> // 使用了其中的 cin cout endl
#include <cstring> // 使用了其中的 strcmp()
// 不要使用 using namespace std;
// 需要啥就 using 啥
using std::cin;
using std::cout;
using std::endl;
// 定义一些宏, 作为状态码使用, 方便知道函数处理结果
// 个人建议使用枚举类
// enum class Status {OK, ERROR, OVERFLOW};
#define OK 1 // 很好, 没有问题
#define ERROR 0 // 操作出现错误
#define OVERFLOW -2 // 溢出, 内存分配失败
typedef int Status; //Status 是函数返回值类型,其值是函数结果状态代码。
#define MAXSIZE 100 //表可能达到的最大长度
typedef struct Student // C++可以不写 typedef, C 需要写
{
char no[8]; //8位学号
char name[80]; //姓名
int price; //成绩
};
//顺序表的定义
typedef struct SqList
{
Student *elem; //指向数据元素的基地址
int length; //线性表的当前长度
};
// --------------------- 下面是顺序表相关函数的声明 ----------------------
Status InitList(SqList &L); //初使化顺序表
Status CreatList(SqList &L); //构建顺序表
Status TraverseList(SqList &L); //显示
Status LocateElem(SqList &L, char *name); //查找
Status GetElem(SqList &L, int i); //获取
Status ListInsert(SqList &L, int i); //插入
Status ListDelete(SqList &L, int i); //删除
Status ListLength(SqList &L); //顺序表长度
Status DestroyList(SqList &L); //销毁顺序表
// -------------- 下面是 main 函数, 程序入口 --------------------------------
int main()
{
SqList L;
int i, choose;
char name[80];
//屏幕提示操作!
cout << "---------------------\n";
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 << "9. 销毁顺序表\n";
cout << "0. 退出\n\n";
cout << "---------------------\n";
choose = -1;
while (choose != 0)
{ //函数调用
cout << "请选择:";
cin >> choose;
switch (choose)
{
case 1: //初始化顺序表
if (InitList(L))
cout << "初始化顺序表成功!" << endl;
else
cout << "初始化顺序表失败!" << endl;
break;
case 2: //构建顺序表
if (CreatList(L))
cout << "构建顺序表成功!" << endl;
else
cout << "构建顺序表失败!" << endl;
break;
case 3: //顺序表长度
cout << "当前顺序表长: " << ListLength(L) << endl;
break;
case 4: //显示顺序表内容
TraverseList(L);
break;
case 5: //查找学生信息
cout << "请输入学生姓名: ";
cin >> name;
LocateElem(L, name);
break;
case 6: //获取学生信息
cout << "请输入学生编号:";
cin >> i;
GetElem(L, i - 1); // 编号-1才是数组下标,编号从 1 开始比较符合普通用户的思维
break;
case 7: //插入学生信息
cout << "请输入要插入学生的编号: ";
cin >> i;
if (ListInsert(L, i - 1))
cout << "信息插入成功!" << endl;
else
cout << "信息插入失败!" << endl;
break;
case 8: //删除学生信息
cout << "请输入要删除学生的编号: " << endl;
cin >> i;
if (ListDelete(L, i - 1))
cout << "删除成功!" << endl;
else
cout << "删除失败!" << endl;
break;
case 9: //销毁顺序表
if (DestroyList(L))
cout << "成功删除顺序表\n\n";
else
cout << "顺序表删除失败\n\n";
break;
}
getchar();
}
}
// --------------------- 下面是顺序表相关的函数定义 -----------------------------------
//1.初始化顺序表
Status InitList(SqList &L)
{
L.elem = new Student[MAXSIZE]; // 分配内存空间
if (!L.elem)
exit(OVERFLOW); // 内存分配失败, 退出程序
L.length = 0;
return OK;
}
//2.创建顺序表
Status CreatList(SqList &L)
{
if (!L.elem)
exit(OVERFLOW); //顺序表不存在退出
do
{
cout << "输入学号,姓名,成绩: ";
cin >> L.elem[L.length].no >> L.elem[L.length].name >> L.elem[L.length].price;
L.length++;
} while (strcmp(L.elem[L.length - 1].no, "0") != 0); //输入0 0 0时结束
L.length--;
return OK;
}
//3.遍历顺序表,显示顺序表内容
Status TraverseList(SqList &L)
{
for (int i = 0; i < L.length; i++)
{
Student &stu = L.elem[i];
cout << "[" << (i + 1) << "]\t姓名:" << stu.name << "\t学号:" << stu.no << "\t成绩:" << stu.price << endl;
}
return OK;
}
//4.查找(通过姓名)
Status LocateElem(SqList &L, char *name)
{
for (int i = 0; i < L.length; i++)
{
Student &stu = L.elem[i];
if (strcmp(stu.name, name) == 0) //不要直接使用 ==, 使用 strcmp() 函数比较字符串是否相等
cout << "姓名:" << stu.name << "\t学号:" << stu.no << "\t成绩:" << stu.price << endl;
}
return OK;
}
//5.获取(通过序号),序号从 0 开始
Status GetElem(SqList &L, int i)
{
if (i < 0 || i >= L.length) // 序号非法
return ERROR;
Student &stu = L.elem[i];
cout << "姓名:" << stu.name << "\t学号:" << stu.no << "\t成绩:" << stu.price << endl;
return OK;
}
//6.插入(在指定位置i插入学生信息)
Status ListInsert(SqList &L, int i)
{
if (i < 0 || i > L.length) // 序号非法
return ERROR;
for (int last = L.length; last > i; last--)
L.elem[last] = L.elem[last - 1]; // i 号元素和后面的元素,全部右移一格,让出 i 号位置
cout << "输入学号,姓名,成绩: ";
cin >> L.elem[i].no >> L.elem[i].name >> L.elem[i].price;
L.length++;
return OK;
}
//7.删除(删除指定位置i的学生信息)
Status ListDelete(SqList &L, int i)
{
if (i < 0 || i >= L.length) // 序号非法
return ERROR;
for (int pos = i; pos < L.length - 1; pos++) // i 号元素后面的往前移一格,覆盖自己前面的元素
L.elem[pos] = L.elem[pos + 1];
L.length--; // 记得 length - 1
return OK;
}
//8.返回顺序表长度
int ListLength(SqList &L)
{
return L.length;
}
//9. 销毁顺序表
Status DestroyList(SqList &L)
{
if (L.elem)
delete[] L.elem; //释放存储空间
L.length = 0;
return OK;
}
运行截图
OOP版
使用 C++11 编译
- 使用模板对顺序表进行封装
- 使用面向对象风格
C++ 基础好点的同学可以看下这个。
#include <iostream>
#include <string>
#include <exception>
using std::cin;
using std::cout;
using std::endl;
using std::string;
using Score = int; // 成绩的类型
struct Student
{
string name; // 姓名
size_t id; // 学号
Score score; // 成绩
};
// 顺序表
// Example:
// Sqlist<Student> stu_list(100);
// Sqlist<int> int_list(100);
template <typename ElemType>
class SqList
{
private:
ElemType *data_list_;
size_t length_; // 当前元素个数
size_t capacity_; // 顺序表容量
public:
SqList(size_t size);
~SqList() { delete[] data_list_; };
SqList(const SqList &) = delete;
SqList &operator=(const SqList &) = delete;
bool IsEmpty() { return length_ == 0; } // 判空
bool IsFull() { return length_ == capacity_; } // 判满
size_t Length() { return length_; } // 获取数据长度
size_t Capacity() { return capacity_; } // 获取顺序表容量
ElemType &operator[](size_t pos) { return data_list_[pos]; } // 取值
bool Append(ElemType elem); // 在顺序表结尾添加元素
bool Insert(size_t pos, ElemType elem); // 在 pos 位置插入一个元素
ElemType Pop(size_t pos); // 弹出 pos 位置的元素
bool Delete(size_t pos); // 删除 pos 位置的元素
};
template <typename ElemType>
SqList<ElemType>::SqList(size_t size) : length_(0), capacity_(size)
{
data_list_ = new ElemType[size];
if (!data_list_)
throw std::bad_alloc(); // 分配内存失败
}
template <typename ElemType>
bool SqList<ElemType>::Append(ElemType elem)
{
if (IsFull())
return false; // 满了
data_list_[length_] = elem;
length_++;
return true;
}
template <typename ElemType>
bool SqList<ElemType>::Insert(size_t pos, ElemType elem)
{
if (pos < 0 || pos > length_ || IsFull())
return false;
for (size_t i = length_; i > pos; --i)
data_list_[i] = data_list_[i - 1]; // pos 之后的元素后移让位
data_list_[pos] = elem;
length_++;
return true;
}
template <typename ElemType>
ElemType SqList<ElemType>::Pop(size_t pos)
{
if (pos < 0 || pos >= length_ || IsEmpty())
throw std::out_of_range("Not this Element");
ElemType tmp = data_list_[pos];
for (size_t i = pos; i < length_; ++i)
data_list_[i] = data_list_[i + 1]; //后面的元素前移
length_--;
return tmp;
}
template <typename ElemType>
bool SqList<ElemType>::Delete(size_t pos)
{
try
{
Pop(pos);
return true;
}
catch (const std::out_of_range)
{
return false;
}
}
// -------------------------------------------------
// 学生信息管理器
class StudentManager
{
private:
SqList<Student> data_;
public:
StudentManager(size_t capacity) : data_(capacity) {}
~StudentManager() = default;
StudentManager(const StudentManager &) = delete;
StudentManager &operator=(const StudentManager &) = delete;
void Run(); // 运行
void ClearScreen(); // 清屏
void ShowMenu(); // 显示主目录
void ShowAllStudentInfo(); // 显示所有学生信息
void RegisterStudent(size_t pos); // 登记一个学生
void InsertStudent(); // 插入一个学生
void DeleteStudent(); // 删除学生
void SearchByName(); // 根据姓名查找学生
void ShowListStatus(); // 显示链表状态
};
void StudentManager::ClearScreen()
{
#if defined(__linux__)
system("clear");
#elif defined(_WIN32)
system("cls");
#endif
}
void StudentManager::ShowAllStudentInfo()
{
if (data_.IsEmpty())
{
cout << "没有学生信息可以删除!" << endl;
return;
}
cout << "序号\t姓名\t学号\t\t成绩" << endl;
for (size_t i = 0; i < data_.Length(); ++i)
cout << "[" << i + 1 << "]\t" << data_[i].name << '\t' << data_[i].id << '\t' << data_[i].score << endl;
}
void StudentManager::RegisterStudent(size_t pos = -1)
{
pos = (pos == -1) ? data_.Length() : pos; // 默认插入尾部
string name;
size_t id;
Score score;
cout << "输入姓名: ";
cin >> name;
cout << "输入学号: ";
cin >> id;
cout << "输入成绩: ";
cin >> score;
Student s = {name, id, score};
if (data_.Insert(pos, s))
cout << "插入数据成功!" << endl;
else
cout << "插入数据失败!" << endl;
}
void StudentManager::InsertStudent()
{
size_t pos;
cout << "当前有" << data_.Length() << "条信息, 输入插入位置: ";
cin >> pos;
if(pos <1 || pos>data_.Length())
{
cout << "输入有误!" << endl;
return;
}
RegisterStudent(pos);
}
void StudentManager::DeleteStudent()
{
if (data_.IsEmpty())
{
cout << "没有学生信息可以删除!" << endl;
return;
}
ShowAllStudentInfo();
size_t index;
cout << "输入学生编号: ";
cin >> index;
if (index < 1 || index > data_.Length())
{
cout << "编号有误!" << endl;
return;
}
if (data_.Delete(index - 1))
cout << "删除成功!" << endl;
else
cout << "删除失败!" << endl;
}
void StudentManager::SearchByName()
{
string name;
cout << "输入学生姓名:";
cin >> name;
bool is_found = false; // 标记是否找到信息
for (size_t i = 0; i < data_.Length(); ++i)
if (data_[i].name == name)
{
cout << "姓名: " << name << "\t学号: " << data_[i].id << "\t成绩: " << data_[i].score << endl;
is_found = true;
}
if (!is_found)
cout << "查无此人: " << name << endl;
}
void StudentManager::ShowListStatus()
{
cout << "学生数量: " << data_.Length() << "/" << data_.Capacity() << endl;
}
void StudentManager::ShowMenu()
{
ClearScreen();
cout << endl;
cout << "=================" << endl;
ShowListStatus();
cout << "=================" << endl;
cout << "1.添加学生信息" << endl;
cout << "2.插入学生信息" << endl;
cout << "3.全部学生信息" << endl;
cout << "4.查找学生信息" << endl;
cout << "5.删除学生信息" << endl;
cout << "6.退出程序" << endl;
cout << "=================" << endl;
cout << endl;
int choose;
cout << "输入序号: ";
cin >> choose;
if (cin.fail())
{
cin.clear();
cin.ignore(100, '\n'); // 清除 cin 的fail的状态, 否则无限循环
return;
}
switch (choose)
{
case 1:
RegisterStudent();
break;
case 2:
InsertStudent();
break;
case 3:
ClearScreen();
ShowAllStudentInfo();
break;
case 4:
SearchByName();
break;
case 5:
ClearScreen();
DeleteStudent();
break;
case 6:
exit(0);
default:
cout << "序号有误!" << endl;
}
// 故意暂停, 回车继续
cout << "\n按 Enter 继续...";
cin.get(); // 读取缓冲区的换行符
cin.get(); // 等待用户按 Enter
}
void StudentManager::Run()
{
while (true)
{
ShowMenu();
}
}
// -------------------------------------------------
// 主函数
int main(int argc, char const *argv[])
{
StudentManager manager(100);
manager.Run();
}
其它
先尝试自己写, 有问题再找我。