图书管理系统(顺序表)的姊妹篇来了。
这次是用单链表实现的。
1、结构体定义
结构体的数据域依然是ISBN、书名和书的价格。
和顺序表的不同是,每个结点除了数据域之外,
还需要存储一个指针(指针域)。
每个结点的结构类型都是——struct BookList,因此,指向结点的指针类型,便是struct BookList* 。
typedef struct BookList
{
int isbn;
char bookName[20];
int price;
struct BookList* next; //指向直接后继
}Node, * List;
2、单链表的创建
这个函数称为CreateList。这很合理。
为了知道创建是否成功,把函数定义为bool类型。
返回值是True 或者 False。
- 返回值为true - 创建链表成功。
- 返回值为false - 创建失败。
传进函数两个参数。
- 链表L //事实上,我们把头结点的名字就称为链表的名字
- n - 需要创建的结点总数。
bool CreateList(List& L, int n)// L - 链表头结点,n - 结点总数
函数体内创建一个for循环,循环次数由结点总数n控制。
for (int i = 0; i < n; ++i) // Q:这和 i++有区别吗?
在循环体中创建一个新的结点book,
Node* book = new Node;
将用户的输入逐个保存到结点book里。
std::cout << “正在添加第” << i + 1 << “本图书--------------\n”;
printf(“请输入图书的ISBN,以回车结束:”);// 这里可以进行ISBN查重
std::cin >> book->isbn;
printf(“请输入书名,以回车结束:”);
std::cin >> book->bookName;
printf(“请输入图书价格,以回车结束:”);
std::cin >> book->price;
有心人可以关注到我在代码里写的注释——“ISBN查重”。这句话是什么意思呢?
让我们联想一下,在以前数据库课程的学习中,一张图书表的主键往往是谁?
主键是书的名字?中文书名包含有中文字符,换一个国家又需要换一种语言。作为主键着实是负担。
主键是书的价格?来看看全世界一共出版过的书的数量。而这个数字还不断的在更新。
谷歌图书终于数出——全世界总共出版过1 2986 4880本书!
要让每本书的定价都不一样,假设用2进制表示,则大约需要2的27次方=1 3421 7728,才能表示完所有图书的价格。
而且,如果书打折了,怎么办?
如果你想到的是图书的ISBN,那就对了。
因此,可以对用户输入的ISBN进行查重检测,如果链表里存在相同ISBN的结点,则提示。伪代码描述如下。
if (Search(L, isbn) != NULL)// 如果找到了相同ISBN的一个结点
{
printf(“已存在相同ISBN的书籍,请重新输入\n”);
return;
}
创建单链表中,最核心的两句代码如下:
// 重点理解这两句指令
book->next = L->next;
L->next = book;
这两句代码,是用来改变上一个结点和新结点的指针域。大概可以这么理解:
- 新节点(book) 指向 上一个结点后面的结点
- 上一个节点 指向 新节点(book)
根据以上思路,最终得到创建单链表的函数:
bool CreateList(List& L, int n)// L - 链表的头结点,n - 结点总数
{
for (int i = 0; i < n; ++i)
{
Node* book = new Node;
std::cout << "正在添加第" << i + 1 << "本图书--------------\n";
printf("请输入图书的ISBN,以回车结束:");// 这里可以进行ISBN查重
std::cin >> book->isbn;
printf("请输入书名,以回车结束:");
std::cin >> book->bookName;
printf("请输入图书价格,以回车结束:");
std::cin >> book->price;
// 重点理解这两句指令
book->next = L->next;
L->next = book;
}
printf("\n\t结束添加\n");
return true;
}
运行结果
-
提示界面
-
添加图书
-
打印图书信息
从运行结果也可以看出,创建单链表用的是头插法。
-
插入图书
默认插在输入序号的位置。
-
查找图书
-
修改图书信息
有ISBN重复的情况就会提示无法编辑。
没有找到重复的ISBN就可以修改成功。 -
删除图书
完整代码
好了,不废话了。放一下完整代码。
#include <iostream>
typedef struct BookList
{
int isbn;
char bookName[20];
int price;
struct BookList* next; //指向直接后继
}Node, * List;
void InitList(List &L)
{
Node* head = new Node;// *head - 头结点; head - 指向头结点的指针
L = head;// 头指针L 指向头结点head
L->next = NULL;
printf("初始化成功\n");
}
bool CreateList(List& L, int n)
{
for (int i = 0; i < n; ++i)
{
Node* book = new Node;
std::cout << "正在添加第" << i + 1 << "本图书--------------\n";
printf("请输入图书的ISBN,以回车结束:");// 这里可以进行ISBN查重
std::cin >> book->isbn;
printf("请输入书名,以回车结束:");
std::cin >> book->bookName;
printf("请输入图书价格,以回车结束:");
std::cin >> book->price;
// 重点理解这两句指令
book->next = L->next;
L->next = book;
}
printf("\n\t结束添加\n");
return true;
}
void Display(List& L)
{
if (L->next == NULL)
{
printf("链表为空!\n");
return;
}
int i = 0;
Node* p = L->next;
while (p)
{
i++;
std::cout << "序号 " << i << " :"
<< " ISBN:" << p->isbn
<< " 书名:" << p->bookName
<< " 价格:" << p->price << "\n";
p = p->next;
}
}
int Count(List& L)
{
int num = 0;
Node* p = L->next;
while (p)
{
p = p->next;
num++;
}
return num;
}
bool Insert(List& L, int i)
{
if (i<1 || i>Count(L) + 1)
return false;
Node* book = new Node;
printf("请输入图书的ISBN,以回车结束:");// 这里也可以ISBN查重
std::cin >> book->isbn;
printf("请输入书名,以回车结束:");
std::cin >> book->bookName;
printf("请输入图书价格,以回车结束:");
std::cin >> book->price;
int j = 0;
Node* p = L;// P指向要插入结点的前驱结点
while (p->next && j != i-1)
{
j++;
p = p->next;
}
book->next = p->next;
p->next = book;
return true;
}
Node* SearchElem(List& L, int isbn)
{
int j = 0;
Node* p = L;
while (p->isbn != isbn && p->next)
{
p = p->next;
j++;
}
if (p->isbn == isbn)
{
return p;
}
else
{
return NULL;
}
}
bool Edit(List& L, int i)
{
if (Count(L) < i || i < 1)// 输入的序号 大于 书籍总数
return false;
int j = 0;
Node* p = L;
while (i != j && p->next)
{
p = p->next;
j++;
}
Node* q = new Node;
printf("请输入图书的ISBN,以回车结束:");
std::cin >> q->isbn;
if (SearchElem(L, q->isbn) != NULL)// ISBN是书籍的主键 需要查重
{
printf("已存在相同ISBN的书籍,请重新输入\n");
return false;
}
printf("请输入书名,以回车结束:");
std::cin >> p->bookName;
printf("请输入图书价格,以回车结束:");
std::cin >> p->price;
p->isbn = q->isbn;
return true;
}
bool Delete(List& L, int i)
{
if (Count(L) < i)
return false;
int j = 0;
Node* p = L;
while (p->next && j != i - 1)
{
p = p->next;
j++;
}
if (j > i - 1)
{
printf("删除失败\n");
return false;
}
Node* q = p->next;// 为什么需要q呢 因为要释放的是p的下一个结点
p->next = p->next->next;// 也可以写成p->next = q->next;
delete q;// 如果写delete p->next,接着直接删除第1条书籍,由于后续结点找不到了,则程序意外终止。
return true;
}
void main()
{
int code;// 操作码 1 - 添加图书 2 - 打印图书信息 3 - 插入图书 4 - 查找图书 5 - 删除图书 6 - 修改图书信息
List L;
int bookID;// 图书序号
int bookNum;// 图书数量
printf("******************************************\n\t欢迎来到图书管理系统\n");
InitList(L);
do
{
//这句可以放循环体外面。放在循环体里面,是为了方便查看操作码含义,减少使用者的记忆负担
std::cout << "\n\t0 - 退出系统\n\t1 - 添加图书\n\t2 - 打印图书信息"
<< "\n\t3 - 插入图书\n\t4 - 查找图书\n\t5 - 删除图书\n\t6 - 修改图书信息"
<< "\n******************************************"
<< "\n\t请输入操作代码,以回车结束:";
std::cin >> code;
switch (code)
{
case 0:
printf("\n\t退出系统\n");
break;
case 1:
printf("\n添加图书\n\n请输入图书数量:");
std::cin >> bookNum;
if (bookNum <= 0)
break;
CreateList(L, bookNum);
break;
case 2:
Display(L);
break;
case 3:
printf("请输入图书插入的序号,以回车结束。\n");
std::cin >> bookID;
/* if (bookID == 0)
{
printf("\n\t图书插入操作结束\n");
break;
}*/
if (Insert(L, bookID) == false)
{
printf("插入位置非法\n");
continue;
}
Display(L);// 输出当前图书信息
printf("\n\t图书插入操作结束\n");
break;
case 4:
printf("请输入需要查找的 ISBN,以回车结束。\n");
std::cin >> bookID;// 这里bookID 存储的值是 ISBN
if (SearchElem(L, bookID) == NULL)
{
printf("图书未找到\n");
}
else
{
Node* q = SearchElem(L, bookID);
std::cout << "图书找到了----------"
//<< "序号 " << j << " : "
<< " ISBN:" << q->isbn
<< " 书名:" << q->bookName
<< " 价格:" << q->price << "\n";
}
printf("\n\t查找结束\n");
break;
case 5:
if (Count(L) == 0) {
printf("图书表为空\n");
break;
}
printf("请输入需要删除的图书序号,以回车结束。\n");
std::cin >> bookID;
/* if (bookID == 0)
break;*/
if (Delete(L, bookID) == false)// 这里已经执行Delete语句 并得到返回值
{
printf("图书无法删除\n");
continue;
}
Display(L);
printf("\n\t图书删除操作结束\n");
continue;
case 6:
if (Count(L) == 0) {
printf("图书表为空\n");
break;
}
printf("请输入需要编辑的图书序号,以回车结束。\n");
std::cin >> bookID;
if (Edit(L, bookID) == false)
{
printf("图书无法编辑\n");
continue;
}
Display(L);
printf("\n\t图书编辑操作结束\n");
continue;
default:
printf("\n\t操作码无效\n");
break;
}
} while (code != 0);
printf("\n感谢使用\n");
}