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();
}

其它

先尝试自己写, 有问题再找我。

posted @ 2020-09-18 00:23  zaxtyson  阅读(641)  评论(0编辑  收藏  举报