数据结构/C语言_线性表之顺序表_图书信息管理系统

最近在看严蔚敏的《数据结构C语言版|第2版》,看完了线性表,写了用顺序表实现的图书信息管理系统。

数据结构(C语言版) 第二章 线性表 知识梳理+作业习题详解

经测试可以成功运行。但是输入小数就会出bug。

  • 操作系统:Windows 10
  • 编译器:Visual Studio 2022 preview

个人撰写,仅供参考。

然后说一下代码实现的主要功能,一共是6个功能。输入数字执行对应功能。

  • 1 - 添加图书
  • 2 - 查找图书
  • 3 - 删除图书
  • 4 - 打印图书信息 (方便查看运行结果)
  • 5 - 插入图书
  • 6 - 修改图书信息

书中对此系统的描述:

【案例分析】 把图书表抽象成一个线性表,每本图书(包括ISBN、书名、定价)作为线性表中的一个元素。

现在写一下自己对两个结构体的理解。

结构体的定义

比如图书馆想新增一本书,那么,先买到这本书,在电脑中登记书的信息,接着把书放到某个书架上。

在计算机中要想创建这本书,就需要为书分配一定的内存空间。分配给书的内存包括两部分,一部分是书本身携带的数据,另一部分是书所在的书架位置。

结构体BookList是用来存储书本身信息的。

BookList是一个用来封装书籍数据的结构体,携带的数据包括:书的ISBN、书名和书的定价。

typedef //重命名
struct BookList//重命名的对象
{
    int ISBN;// 书的ISBN
    char bookName[20];// 书名
    int price;// 书的定价
}
Book;//新名字

有了封装好的书之后,接着要确定书放在哪个书架上。

在计算机中,也就是确定书所在的内存地址。

由于顺序表的特性,书的位置在内存中是相邻的,因而可以将顺序表看成一个数组。

an = a1 + (n-1) * q
an—— 第n本书所在地址
a1—— 第1本书所在地址
q—— 1本书所占的内存空间

为数组中的每本书分配一个下标elem,并存储当前总共的书籍数量n,如此封装之后,便得到一个新的结构体。

typedef struct List
{
    Book* elem;// 存储空间的基地址
    int number;// n
}List;

函数解释

  • 插入函数Insert

通过掌握这个函数,理解顺序表的存储方式。

首先用if判断是否达到存储空间上限。

if (当前元素数量加一 大于 上限)
        return;

如果if中是这样的:

 if(L.number + 1 > MAXSIZE)

L.number将会逻辑加1。导致还没有插入元素,元素数量就莫名其妙多出来一个。

所以需要定义一个局部变量num,将L.number+1赋值给num。

int num = L.number + 1;
 if (num > MAXSIZE)
        return false;

另一个注意点——元素的挪动

这里可以联想实际生活中的排队,我们把要插入的元素看做小明同学。小明要么排在队伍最后,要么插队。当小明排在队伍最后,其他人不需要移动。如果小明插队,则小明之后的人都需要往后移动一格。

因而,在顺序表中插入一个元素时,也考虑2种情况:

第1种情况,元素插在队尾。此时不需要挪动其他元素,整体元素数量+1。

    顺序表的[队尾] = 小明;
    顺序表的总个数++;

在结构体中,可以把 “.” 运算符读作 “的” 。把上面的 “的” 换成 “.”

    顺序表.[队尾] = 小明;
    顺序表.总个数++;

再把“顺序表”换成顺序表的变量名“L”,“总个数”换成“number”,“小明”换成“book”,“队尾下标”换成“i-1”(存储的第1个元素下标为0,第2个元素下标是1,…,第i个元素的下标是i-1)。

L.elem[i-1] = book;
    L.number++;

第2种情况,元素不插在队尾。此时它后面的所有元素都需要往后挪动一格。

移动的次序是什么样的呢?继续想象一个队伍。
队伍最后一个人往后移动一格,
倒数第二个人往后移动一格,
…,
小明之后的所有人都往后移动了一格,
队伍出现了一个空位,
小明进入空位,
小明插队成功。

移动的步骤,使用一个for循环,把小明后面的元素统统往后挪动一格。从最后一个人开始,到小明之后的那1个人(包括此人)结束。

for (int j = L.number-1; j >= i-1; j--)//从后往前挪动存储单元
    {
        L.elem[j+1] = L.elem[j];
    }

最后插入小明。这和第一种情况的插入方式是一样的,可以合并。

L.elem[i-1] = book;
    L.number++;

因此,挪动元素、插入元素、总数+1合起来的代码是这样的。

 for (int j = L.number-1; j >= i-1; j--)//从后往前挪动存储单元
    {
        L.elem[j+1] = L.elem[j];
    }
    L.elem[i-1] = book;
    L.number++;

最后把整个函数合在一起。

bool Insert(List& L, int i)
{
    // 如果使用if(L.number + 1 > MAXSIZE)语句,L.number将会逻辑加1,从而导致输出错误。所以需要定义一个局部变量num,将L.number+1赋值给num。
    if (int num = L.number + 1 > MAXSIZE)
        return false;

    Book book;// 保存用户输入的数据
    printf("请输入图书的ISBN,以回车结束:");
    std::cin >> book.isbn;
    printf("请输入书名,以回车结束:");
    std::cin >> book.bookName;
    printf("请输入图书价格,以回车结束:");
    std::cin >> book.price;
    
    for (int j = L.number-1; j >= i-1; j--)//从后往前挪动存储单元
    {
        L.elem[j+1] = L.elem[j];
    }
    L.elem[i-1] = book;
    L.number++;
    return true;
}

运行结果

  • 主提示
    tips

  • 添加图书:
    在这里插入图片描述

  • 查找图书
    search

  • 删除图书(故意输了一个错值)delete

  • 源码

#include <iostream>
#define MAXSIZE 100
typedef struct BookList
{
    int isbn;
    char bookName[20];
    int price;
}Book;
typedef struct List
{
    Book* elem;// 存储空间的基地址(这句注释是抄了书上的)
    int number;
}List;

void initList(List &L)
{
    L.elem = new Book[MAXSIZE];
    if (!L.elem)
        printf("初始化失败\n");
    L.number = 0;
    printf("初始化成功\n");
}

bool SearchElem(List& L, int i)
{
    if (i < 1)
    {
        printf("输入的图书序号为负。当前图书数量:%d\n",L.number);
        return false;
    }
    if (i > L.number)
    {
        printf("列表中不存在此图书。当前图书数量:%d\n", L.number);
        return false;
    }
    Book* p = &L.elem[i - 1];
    printf("找到图书----------------------\n");
    std::cout << "ISBN:" << p->isbn << " 书名:" << p->bookName << " 价格:" << p->price << "\n";
    return true;
}

void Add(List& L, int n)
{
    int i = 0;
    while (i < n && L.number != MAXSIZE)
    {
        Book book;//创建结构体BookList的一个成员book 存储用户输入的数据信息
        std::cout << "正在添加第" << i+1 << "本图书:\n";
        printf("请输入图书的ISBN,以回车结束:");
        std::cin >> book.isbn;
        printf("请输入书名,以回车结束:");
        std::cin >> book.bookName;
        printf("请输入图书价格,以回车结束:");
        std::cin >> book.price;

        L.elem[L.number] = book;   //不是i-1
        L.number++;//书籍数量+1
        i++;
        
        std::cout << "添加图书成功!\n" << "当前书籍数量:" << L.number << "\n";
    }
    printf("结束添加\n");
}

bool Insert(List& L, int i)
{
    // 如果用if(L.number + 1 > MAXSIZE)语句,L.number将会逻辑加1,从而导致输出错误
    if (int num = L.number + 1 > MAXSIZE)
        return false;

    Book book;
    printf("请输入图书的ISBN,以回车结束:");
    std::cin >> book.isbn;
    printf("请输入书名,以回车结束:");
    std::cin >> book.bookName;
    printf("请输入图书价格,以回车结束:");
    std::cin >> book.price;
    
    for (int j = L.number-1; j >= i-1; j--)//从后往前挪动存储单元
    {
        L.elem[j+1] = L.elem[j];
    }
    L.elem[i-1] = book;
    L.number++;
    return true;
}

bool Delete(List& L, int i)
{
    if (i > L.number || i < 1)
    {
        printf("无法删除该图书,因为该图书不存在\n");
        return false;
    }
    for (int j = i; j < L.number; j++)
    {
        L.elem[j-1] = L.elem[j];
    }
    L.number--;
    printf("删除成功\n");
    return true;
}

void DisPlay(List& L) 
{
    int i = 0;
    while (i < L.number) 
    {
        Book book = L.elem[i];
        std::cout << i + 1 <<" :"
            << " ISBN:" << book.isbn
            << " 书名:" << book.bookName
            << " 价格:" << book.price<< "\n";
        i++;
    }
}

bool Edit(List& L,int i) 
{
    if (SearchElem(L, i)) 
    {
        Book book;
        printf("请输入图书的ISBN,以回车结束:");
        std::cin >> book.isbn;
        printf("请输入书名,以回车结束:");
        std::cin >> book.bookName;
        printf("请输入图书价格,以回车结束:");
        std::cin >> book.price;

        L.elem[i - 1] = book;
    }
    DisPlay(L);
    return true;
}

int main()
{
    int code;// 操作码 1 - 添加图书 2 - 查找图书 3 - 删除图书 4 - 打印图书信息
    List L;
    int bookID;// 图书序号
    int bookNum;// 图书数量

    printf("欢迎来到图书管理系统\n");
    initList(L);
    std::cout << "0 - 退出系统\n1 - 添加图书\n2 - 查找图书\n3 - 删除图书\n4 - 打印图书信息\n"
        << "5 - 插入图书\n6 - 修改图书信息\n"
        << "请输入操作代码,以回车结束:\n";
    std::cin >> code;
    do 
    {
        switch (code)
        {
        case 0:
            printf("退出系统\n");
            break;
        case 1:
            printf("添加图书\n默认按照输入的先后顺序进行添加\n请输入添加的图书数量:");
            std::cin >> bookNum;
            if (bookNum <= 0)
                break;
            Add(L, bookNum);
            break;
        case 2:
            printf("请输入需要查找的图书序号,以回车结束。(输入数字0退出)\n");
            std::cin >> bookID;
            if (bookID == 0)
                break;
            if (bookID > L.number || SearchElem(L, bookID) == false)
            {
                printf("图书未找到\n");
                continue;
            }
            continue;
        case 3:
            if (L.number == 0){
                printf("图书表为空\n");
                break;
            }
            printf("请输入需要删除的图书序号,以回车结束。(输入数字0退出)\n");
            std::cin >> bookID;
            if (bookID == 0)
                break;
            if (bookID > L.number || Delete(L, bookID) == false)// 这里已经执行Delete函数 并得到返回值
            {
                printf("图书无法删除\n");
                continue;
            }
            DisPlay(L);
            continue;
        case 4:
            DisPlay(L);
            break;
        case 5:
            printf("请输入插入序号,以回车结束。(输入数字0退出)\n");
            std::cin >> bookID;
            if (bookID == 0)
                break;
            if (bookID<0 || bookID> L.number + 1|| Insert(L, bookID)==false)// 这里已经执行Insert函数 并得到返回值
            {
                printf("插入位置非法\n");
                continue;
            }
            DisPlay(L);// 输出当前图书信息
            continue;
        case 6:
            DisPlay(L);
            printf("请输入修改图书序号,以回车结束。\n");
            std::cin >> bookID;
            if (bookID<0 || bookID> L.number + 1 || Edit(L, bookID) == false)
            {
                printf("修改失败\n");
                continue;
            }
            break;
        default:
            printf("操作码无效\n");
            break;
        }
        printf("请输入操作码,以回车结束:\n");
        std::cin >> code;
    } while (code != 0);

    printf("感谢使用");
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值