数据结构 基于线性表的图书信息管理

  • 实验前的准备
    • IDE的选择
    • C语言中指针与结构体
  • 实验目的
  • 实验内容
    • 1.基于顺序存储结构的图书信息表的创建和输出
      • 代码
      • 实验中遇到的问题 ①
      • 实验中遇到的问题 ②
    • 6.基于顺序存储结构的图书信息表的最爱图书的查找
      • 代码
      • 实验中遇到的问题
    • 8.基于顺序存储结构的图书信息表的新图书的入库
      • 代码
      • 实验中遇到的问题 ①
      • 实验中遇到的问题 ②
      • 实验中遇到的问题 ③
    • 11.基于链式存储结构的图书信息表的创建和输出
      • 代码
      • 实验中遇到的问题 ①
      • 实验中遇到的问题 ②
      • 实验中遇到的问题 ③
    • 15.基于链式存储结构的图书信息表的价格最贵图书的查找
      • 代码
      • 实验中遇到的问题 ①
      • 实验中遇到的问题 ②
    • 19.基于链式存储结构的图书信息表的旧图书的出库
      • 代码
      • 实验中遇到的问题 ①
      • 实验中遇到的问题 ②
  • 个人小结

实验前的准备

IDE的选择

采用的IDE是Visual_Studio_2019,这里需要关掉SDL检查


解决scanf报错的其他方法

1、在程序最前面加:

#define _CRT_SECURE_NO_DEPRECATE

2、在程序最前面加:

#pragma warning(disable:4996)

关于OVERFLOW
一般需要在程序最前面:

#define OVERFLOW -2

但是注意,在VS2019这个IDE中,OVERFLOW已经有默认值3了,所以不需要上述操作,不然会报错;而在其他的编译器上运行时,则需要加上这一句。
为了可移植性考虑,我删去程序中的OVERFLOW,返回值直接写-2。

C语言中指针与结构体

C语言指针介绍
c语言入门 结构体与指针.
typedef与struct
搞懂C语言各种指针、NULL指针、零指针、野指针、悬垂指针
C语言“悬空指针”和“野指针”究竟是什么意思?

int main(int argc, char* argv[])
//ARGc和ARGv中的ARG指的是"参数"
//(外语:ARGuments, argument counter 和 argument vector )

实验目的

1.掌握线性表的顺序存储表示和链式存储表示
2.掌握顺序表和链表的基本操作,包括创建、查找、插入和删除等算法
3.明确线性表两种不同存储结构的特点及其适用场合,明确它们各自的优缺点

实验内容

图书信息表包括以下10项常用的基本操作:图书信息表的创建和输出、排序、修改.逆序存者、最贵图书的查找、最爱图书的查找、最佳位置图书的查找、新图书的人库、旧图书的出库、图书去重。实验要求分别利用顺序表和链表实现上述10 项操作,因此,实验内容总计包括以下20道题目,其中前10道要求基于顺序表实现相应的功能,后10道要求基于链表实现相应的功能。

1.基于顺序存储结构的图书信息表的创建和输出

问题描述

定义一个包含图书信息(书号、书名、价格)的顺序表,读人相应的图书数据来完成图书信息表的创建。然后,统计图书表中的图书个数,同时逐行输出每本图书的信息。

输人要求

输入n+1行,其中前n行是n本图书的信息(书号、书名、价格),每本图书信息占一行, 书号、书名、价格用空格分隔,价格之后没有空格。最后第n+1行是输人结束标志: 0 0 0 (空格分隔的三个0)。其中,书号和书名为字符串类型,价格为浮点数类型。

输出要求

总计n+1行,第1行是所创建的图书信息表中的图书个数,后n行是n本图书的信息(书号、书名、价格),每本图书信息占一行,书号、书名、价格用空格分隔。其中,价格输出保留两位小数。输入样例

9787302257646 程序设计基础 25.00
9787302164340 程序设计基础(第2版) 20.00
9787302219972 单片机技术及应用 32.00
9787302203513 单片机原理与应用技术 26.00
9787810827430 工业计算机控制技术——原理与应用 29.00
9787811234923 汇编语言程序设计教程 21.00
0 0 0

输出样例

6
9787302257646 程序设计基础 25.00
9787302164340 程序设计基础(第2版) 20.00
9787302219972 单片机技术及应用 32.00
9787302203513 单片机原理与应用技术 26.00
9787810827430 工业计算机控制技术——原理与应用29.00
9787811234923 汇编语言程序设计教程 21.00

代码

#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <iostream>
#define MAXSIZE 100
#define OK 1
#define ERROR 0
//typedef Book Elemtype; 不能写在这里,要写在下面
typedef int Status;
typedef struct {char no[20];               //图书ISBNchar name[50];             //图书名字float price;               //图书价格
}Book;
typedef struct {Book* elem;                //存储空间的基地址int length;                //图书表中当前图书个数
}SqList;                       //图书馆的顺序存储结构类型为SqList
typedef Book Elemtype;//Book a[50];
//SqList c[50];
//SqList* L = c;
//SqList t;
//顺序表的初始化
Status InitList(SqList* L) {//L->elem = (Elemtype*)malloc(sizeof(Elemtype)*MAXSIZE);L->elem = new Book[MAXSIZE];//L->elem = (int*)malloc(sizeof(int) * MAXSIZE);//不行的,要用Book*//L->elem = (int*) new int[MAXSIZE];//不行的,原因同上//malloc的返回值是一个指针,指向一段可用内存的起始地址if (!L->elem)exit(-2);L->length = 0;return OK;
}
//以下为自创的线性表插入......emmmmmm
/*
int main() {//SqList *L;InitList(L);for (L->length = 0; L->length < MAXSIZE; L->length++) {scanf("%s %s %f", &(L + L->length)->elem->no, &(L + L->length)->elem->name, &(L + L->length)->elem->price);if ((L + L->length)->elem->no == 0 && (L + L->length)->elem->name == 0 && (L + L->length)->elem->price == 0)break;}printf("%d\n", L->length);for (int i = 0; i < L->length; i++) {printf("%s %s %.2f\n", (L + i)->elem->name, (L + i)->elem->no, (L + i)->elem->price);}}*///以下为课表上的线性表插入,但是发现第一题似乎用不上
Status ListInsert(SqList* L, int i, Book e) {//如果是Elemtype e//PS 原来Elemtype是int定义的,实际上要用Book定义//typedef Book Elemtype;//或者int e,L->elem[i-1]会报错//在顺序表L中第i个位置之前插入新的元素e,i值的合法范围是1~L->length+1if ((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;//表长+1return OK;
}int main() {int i;SqList c[MAXSIZE];SqList* L = c;//Book e;InitList(L);Book b[MAXSIZE];for (i = 0; i < MAXSIZE; i++) {scanf("%s %s %f", &b[i].no, &b[i].name, &b[i].price);//if (strcmp(b[i].no, 0) && strcmp(b[i].name, 0) && (b[i].price == 0)) {break;}//if (b[i].no == '0' && b[i].name == 0 && b[i].price == 0)  break;if (!strcmp(b[i].no, "0") && !strcmp(b[i].name, "0") && b[i].price == 0.0) break;//!strcmp(b[i].no, 0)这个写法是不对的,因为这个只能执行一行//!strcmp(b[i].no, '0')这个写法编译都过不了}printf("%d\n", i);for (int j = 0; j < i; j++) {printf("%s %s %.2f\n", b[j].no, b[j].name, b[j].price);}//ListInsert(L, i, e);return 0;
}

实验中遇到的问题 ①

有特别多想说的。首先是SqList * L;
这样定义指针而不赋初始值,是会在运行的时候出错的。
SqList c[MAXSIZE];
SqList
L = c;*
必须这样写,不然的话,运行时会闪退(无论用什么版本的编译器)。

很显然,只有SqList *L;
这一句,无论是在全局范围内还是在主函数中,都是定义了一个空指针or野指针,就像VS里面说的 L是nullptr

在vc的debug模式下,对于未初始化的栈内存全部填成0xcc,对应于MBCS编码中汉字字符串看就是烫烫烫烫;对于未初始化的队内存全部填成0xcd,对应于汉子字符串看就是屯屯屯屯;release模式下直接就是内存中随机的数据。

结构体指针也可以作为函数的参数来使用
这里需要注意的是
使用结构体变量获取成员使用 . 运算符
使用结构体指针获取成员使用 -> 运算符
如果我们不使用结构体变量声明,而通过malloc函数来创建结构体的话,得到的就是指向开辟空间的地址的结构体

我原来的想法是:
我原来定义了一个结构体
typedef struct{
}SqList;
然后再SqList *L;
我以为L就是指向结构体的指针了
没想到这样定义是空指针

我们通常说的结构体指针实际上是指指向结构体类型数据的指针

结构体是我们新定义的数据类型和int,char,float这些关键字一样只是表示类型,不会占用内存空间,而结构体变量或者结构体数据才是实实在在创建出来的数据,有内存空间,我们可以创建指针指向结构体数据,但是不能指向结构体本身,我们说的结构体指针指的是指向结构体类型数据的指针而不是指向结构体的指针

除了这一种方法,我们还可以直接 SqList L;
然后通过引用的方法,避免直接定义 指针 * L

采用引用的方法如下。
引用法一

#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <iostream>
#define MAXSIZE 100
#define OK 1
#define ERROR 0
typedef int Status;
typedef struct {char no[20];               //图书ISBNchar name[50];             //图书名字float price;               //图书价格
}Book;
typedef struct {Book* elem;                //存储空间的基地址int length;                //图书表中当前图书个数
}SqList;                       //图书馆的顺序存储结构类型为SqList
typedef Book Elemtype;
Status InitList(SqList* L) {//L->elem = (Elemtype*)malloc(sizeof(Elemtype)*MAXSIZE);L->elem = new Book[MAXSIZE];//malloc的返回值是一个指针,指向一段可用内存的起始地址if (!L->elem)exit(-2);L->length = 0;return OK;
}int main() {int i;SqList L;InitList(&L);Book b[MAXSIZE];for (i = 0; i < MAXSIZE; i++) {scanf("%s %s %f", &b[i].no, &b[i].name, &b[i].price);if (!strcmp(b[i].no, "0") && !strcmp(b[i].name, "0") && (b[i].price == 0)) break;}printf("%d\n", i );for (int j = 0; j < i; j++) {printf("%s %s %.2f\n", b[j].no, b[j].name, b[j].price);}
}

法一这种在主函数中用到引用&符号的 ,在胡凡的算法笔记中没有提及。

InitList(&L);
Status InitList(SqList* L){}

引用法二

#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <iostream>
#define MAXSIZE 100
#define OK 1
#define ERROR 0
typedef int Status;
typedef struct {char no[20];               //图书ISBNchar name[50];             //图书名字float price;               //图书价格
}Book;
typedef struct {Book* elem;                //存储空间的基地址int length;                //图书表中当前图书个数
}SqList;                       //图书馆的顺序存储结构类型为SqList
typedef Book Elemtype;
Status InitList(SqList& L) {//L->elem = (Elemtype*)malloc(sizeof(Elemtype)*MAXSIZE);L.elem = new Book[MAXSIZE];//malloc的返回值是一个指针,指向一段可用内存的起始地址if (!L.elem)exit(-2);L.length = 0;return OK;
}int main() {int i;SqList L;InitList(L);Book b[MAXSIZE];for (i = 0; i < MAXSIZE; i++) {scanf("%s %s %f", &b[i].no, &b[i].name, &b[i].price);if (!strcmp(b[i].no, "0") && !strcmp(b[i].name, "0") && (b[i].price == 0)) break;}printf("%d\n", i );for (int j = 0; j < i; j++) {printf("%s %s %.2f\n", b[j].no, b[j].name, b[j].price);}
}

法二用到的函数引用模型类型如下:

#include<stdio.h>
void change(int &x){x=1;
}
int main(){int a = 10;change(a);printf("%d\n",a);return 0;
}

实验中遇到的问题 ②

关于strcmp()函数

if (!strcmp(b[i].no, "0") && !strcmp(b[i].name, "0") && (b[i].price == 0))
break;

首先,这里!和“0”,都是一开始我错的。
如果是!strcmp(b[i].no, 0) 这个只能输入一行就break了
如果是!strcmp(b[i].no, ‘0’) 这个编译都过不了
一开始我还漏了!,说明对strcmp函数最后的输出不够了解。

比较两个字符串。设这两个字符串为str1,str2,若str1=str2,则返回零;若str1>str2,则返回正数;若str1<str2,则返回负数。

strcmp函数的介绍
strcmp函数相等是返回0的。

6.基于顺序存储结构的图书信息表的最爱图书的查找

问题描述
定义一个包含图书信息(书号、书名、价格)的顺序表,读人相应的图书数据来完成图书信息表的创建。然后,根据指定的最爱图书的名字,查找最爱的图书,输出相应图书的信息。
输人要求
总计n+m+2行。首先,输入n+1行,其中,第一行是图书数目n,后n行是n本图书的信息(书号、书名、价格),每本图书信息占一行,书号、书名、价格用空格分隔,价格之后没有空格。其中书号和书名为字符串类型,价格为浮点数类型。然后输人m+1行,其中,第一行是一个整数m,代表查找m次,后m行是每次待查找的最爱图书名字。
输出要求
若查找成功:
总计输出m(k+1)行,对于每一次查找,第一行是最爱图书数目(同一书名的图书可能有多本),后k行是最爱图书的信息(书号、书名、价格),每本图书信息占一行,书号、书名、价格用空格分隔。其中,价格输出保留两位小数。
若查找失败:
只输出以下提示:抱歉,没有你的最爱!
输入样例
6
9787302257646 程序设计基础 25.00
9787302164340 程序设计基础(第2版) 20.00
9787302219972 单片机技术及应用 32.00
9787302203513 单片机原理与应用技术 26.00
9787810827430 工业计算机控制技术——原理与应用 29.00
9787811234923 汇编语言程序设计教程 21.00
2
数据结构
程序设计基础
输出样例
抱歉,没有你的最爱!
1
9787302257646 程序设计基础 25.00

代码

#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
#include<iostream>
using namespace std;
#define MAXSIZE 100
#define OK 1
#define ERROR 0
typedef int Status;
typedef struct {char no[20];               //图书ISBNchar name[50];             //图书名字float price;               //图书价格
}Book;
typedef struct {Book* elem;                //存储空间的基地址int length;                //图书表中当前图书个数
}SqList;                       //图书馆的顺序存储结构类型为SqList
typedef Book Elemtype;
Status InitList(SqList* L) {L->elem = (Elemtype*)malloc(sizeof(Elemtype) * MAXSIZE);//malloc的返回值是一个指针,指向一段可用内存的起始地址if (!L->elem)exit(-2);L->length = 0;return OK;
}
int main() {int n;//图书书目int i, j;Book b[MAXSIZE];scanf("%d", &n);SqList L;InitList(&L);for (i = 0; i < n; i++) {scanf("%s %s %f", &b[i].no, &b[i].name, &b[i].price);}int m;//查找m次scanf("%d", &m);Book b_1[MAXSIZE];for (int k = 0; k < m; k++) {scanf("%s", &b_1[k].name);}for (int k = 0; k < m; k++) {for (j = 0; j < i; j++) {//printf("1%s\n", b_1[k].name);//printf("2%s\n", b[j].name);//if (b_1[k].name == b[j].name) {if (!strcmp(b_1[k].name , b[j].name)) {printf("%d\n", j + 1);printf("%s %s %.2f\n", b[j].no, b[j].name, b[j].price);break;}if (j == i - 1 && b_1[k].name != b[j].name) {printf("抱歉,这里没有你的最爱!\n");}}}
}

在实验1.1的基础上做的,难度不是很大。

实验中遇到的问题

还是有关于strcmp()函数的问题。
字符串比较。

if (b_1[k].name == b[j].name)
if (!strcmp(b_1[k].name , b[j].name))

这里的字符串相等,不能用==,就算字符串相同也不能返回真;需要采用strcmp()函数来解决。

8.基于顺序存储结构的图书信息表的新图书的入库

问题描述
首先,定义一个包含图书信息(书号、书名、价格)的顺序表,读入相应的图书数据来完成图书信息表的创建。然后,根据指定的待入库的新图书的位置和信息,将新图书插入到图书表中指定的位置上。最后,输出新图书入库后所有图书的信息。
输入要求
总计n+3行。首先,输入n+1行,其中,第一行是图书数目n,后n行是n本图书的信息(书号、书名、价格),每本图书信息占一行,书号、书名、价格用空格分隔,价格之后没有空格。其中,书号和书名为字符串类型,价格为浮点数类型。之后输入第n+2行,内容仅为一个整数,代表待入库的新图书的位置序号。最后,输入第n+3行,内容为新图书的信息,书号、书名、价格用空格分隔。
输出要求
若插入成功:
输出新图书入库后所有图书的信息(书号、书名、价格),总计n+1行,每行是一本图书的信息,书号、书名、价格用空格分隔。其中,价格输出保留两位小数。
若插入失败:
只输出以下提示:抱歉,入库位置非法!
输入样例
6
9787302257646 程序设计基础 25.00
9787302164340 程序设计基础(第2版) 20.00
9787302219972 单片机技术及应用 32.00
9787302203513 单片机原理与应用技术 26.00
9787810827430工业计算机控制技术——原理与应用 29.00
9787811234923 汇编语言程序设计教程 21.00
2
9787302265436 计算机导论实验指导 18.00
输出样例
9787302257646 程序设计基础 25.00
9787302265436 计算机导论实验指导 18.00
9787302164340 程序设计基础(第2版) 20.00
9787302219972 单片机技术及应用 32.00
9787302203513 单片机原理与应用技术 26.00
9787810827430工业计算机控制技术——原理与应用 29.00
9787811234923 汇编语言程序设计教程 21.00

代码

#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
#include<iostream>
using namespace std;
#define MAXSIZE 100
#define OK 1
#define ERROR 0
typedef int Status;
typedef struct {char no[20];               //图书ISBNchar name[50];             //图书名字float price;               //图书价格
}Book;
typedef struct {Book* elem;                //存储空间的基地址int length;                //图书表中当前图书个数
}SqList;                       //图书馆的顺序存储结构类型为SqList
typedef Book Elemtype;
//顺序表的初始化
Status InitList(SqList* L) {L->elem = (Elemtype*)malloc(sizeof(Elemtype) * MAXSIZE);//malloc的返回值是一个指针,指向一段可用内存的起始地址if (!L->elem)exit(-2);L->length = 0;return OK;
}
//顺序表的插入
Status ListInsert(SqList& L, int i, Elemtype e) {//在顺序表L中第i个位置之前插入新的元素e,i值的合法范围是1<=i<=L.length+1if ((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;//表长加1return OK;
}
int main() {int n;//图书书目int i;Book b[MAXSIZE];scanf("%d", &n);SqList L;InitList(&L);for (i = 0; i < n; i++) {scanf("%s %s %f", &b[i].no, &b[i].name, &b[i].price);}L.length = n;int m;//插入的位置scanf("%d", &m);//输入第n+2行Book in_b;//插入的内容//scanf("%s %s %f", &in_b.no, &in_b.name, &in_b.price);scanf("%s %s %f", in_b.no, in_b.name, &in_b.price);/*scanf("%s", &in_b.no);scanf("%s", &in_b.name);scanf("%f", &in_b.price);*/L.elem = b;ListInsert(L, m, in_b);//ListInsert(L, m, b[m]); for (int k = 0; k < n + 1; k++){printf("%s %s %.2f\n", b[k].no, b[k].name, b[k].price);}
}

实验中遇到的问题 ①

依然是指针,引用,函数之间的关系
如何运用ListInsert这个函数模块十分关键。

ListInsert(L, m, in_b);

这里吹一下,VS2019的单步调试真的方便,我第一次写的时候,漏了两个地方。
一个是:

L.elem = b;

另一个是:

L.length = n;

可以看出来,我在定义SqList L;之后几乎没怎么用过它,逐过程中,发现L数组内容都是乱码,L.length也没有赋值,还处于初始化=0的状态。

实验中遇到的问题 ②

指针,引用,函数之间的关系
到另一个关键处了。
一开始我写的是:

ListInsert(L, m, b[m]);

我在写完L.elem = b;L.length = n;又改了运行一遍,发现可行。所以还是L一开始的指针指向,已经初始化后,长度并没有赋值的问题。
如下写法也是可行的:

Book p = *(b + m);
ListInsert(L, m, p);

下面这个是运行正确的:

ListInsert(L, m, in_b);

实验中遇到的问题 ③

在总结的过程中,突然check出来运行的错误,我的实例是把第2行的内容插入进去,但是事与愿违。
诶,不对!
我发现,ListInsert(L, m, in_b);写法运行出来的结果是对的,其他两个运行出来的都是错误的。
我一开始猜测应该是m大小的问题,估计要减1,才导致了另外两个方法在第三个参数时,运用到m导致出错。
但其实不是,这两种方法(用到b[m] 和 p的)最大的问题是,插入的语句根本没有输入进去,而in_b是直接定义了一个Book类型的变量,然后把语句输入这个结构体变量中,再插入语句。b[m]和p=*(b+m)都是一个意思,也都只在第一次输入的数组中打转,根本没有输入插入的内容!
正确的运行结果:

错误的运行结果:

错误的最大原因就是这样写的,导致插入的内容都没有进入到ListInsert函数。

11.基于链式存储结构的图书信息表的创建和输出

问题描述
定义一个包含图书信息(书号、书名、价格)的链表,读入相应的图书数据来完成图书信息表的创建。然后,统计图书表中的图书个数,同时逐行输出每本图书的信息。
输入要求
输入n+1行,其中前n行是n本图书的信息(书号、书名、价格),每本图书信息占一行,书号、书名、价格用空格分隔,价格之后没有空格。最后第n+1行是输入结束标志: 0 0 0(空格分隔的三个0)。其中,书号和书名为字符串类型,价格为浮点数类型。
输出要求
总计n+1行,第1行是所创建的图书信息表中的图书个数,后n行是n本图书的信息(书号、书名、价格),每本图书信息占一行,书号、书名、价格用空格分隔。其中,价格输出保留两位小数。
输入样例
9787302257646 程序设计基础 25.00
9787302164340 程序设计基础(第2版) 20.00
9787302219972 单片机技术及应用 32.00
9787302203513 单片机原理与应用技术 26.00
9787810827430 工业计算机控制技术——原理与应用 29.00
9787811234923 汇编语言程序设计教程 21.00
0 0 0
输出样例
6
9787302257646 程序设计基础 25.00
9787302164340 程序设计基础(第2版) 20.00
9787302219972 单片机技术及应用 32.00
9787302203513 单片机原理与应用技术 26.00
9787810827430 工业计算机控制技术——原理与应用 29.00
9787811234923 汇编语言程序设计教程 21.00

代码

#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
#include<iostream>
using namespace std;
#define MAXSIZE 100
#define OK 1
#define ERROR 0
typedef int Status;
int i;
typedef struct {char no[20];               //图书ISBNchar name[50];             //图书名字float price;               //图书价格
}Book;
typedef Book Elemtype;//————————————单链表的存储结构————————————
typedef struct LNode {Elemtype data;//结点的数据域struct LNode* next;//结点的指针域
}LNode, * LinkList;//LinkList为指向结构体LNode的指针类型
//构造一个空的单链表
Status InitList(LinkList& L) {//L = (LNode*)malloc(sizeof(LNode));//生成新结点作为头结点,用头指针L指向头结点//L = (LinkList)malloc(sizeof(LNode));//这种写法也可以L = new LNode;L->next = NULL;return OK;
}//后插法,才是题目所要的次序
void CreateList_H(LinkList& L) {//正位序输入n个元素的值,建立带表头结点的单链表LL = new LNode;L->next = NULL;  //先建立一个带头结点的空链表LinkList r = L;           //尾指针r指向头结点for (i = 0; i < MAXSIZE; i++) {LinkList p = new LNode;scanf("%s %s %f", &p->data.no, &p->data.name, &p->data.price);if (!(strcmp(p->data.no, "0")) && !(strcmp(p->data.name, "0")) && p->data.price == 0) {break;}p->next = NULL;r->next = p;r = p;}
}int main() {int  j;LNode* L;InitList(L);CreateList_H(L);for (j = 0; j < i; j++) {L = L->next;printf("%s %s %.2f\n", L->data.no, L->data.name, L->data.price);}
}

错误范例:下面这个代码不是错了,而是根本创建链表,用类似数组的性质和指针在做。

#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
#include<iostream>
using namespace std;
#define MAXSIZE 100
#define OK 1
#define ERROR 0
typedef int Status;typedef struct {char no[20];               //图书ISBNchar name[50];             //图书名字float price;               //图书价格
}Book;
typedef Book Elemtype;//————————————单链表的存储结构————————————
typedef struct LNode {Elemtype data;//结点的数据域struct LNode* next;//结点的指针域
}LNode, * LinkList;//LinkList为指向结构体LNode的指针类型
//构造一个空的单链表
Status InitList(LinkList& L) {L = (LNode*)malloc(sizeof(LNode));//生成新结点作为头结点,用头指针L指向头结点//L = (LinkList)malloc(sizeof(LNode));//这种写法也可以L->next = NULL;return OK;
}
int main() {int i, j;LNode* L = NULL;InitList(L);for (i = 0; i < MAXSIZE; i++) {scanf("%s %s %f", &(L + i)->data.no, &(L + i)->data.name, &(L + i)->data.price);if (!(strcmp((L + i)->data.no, "0")) && !(strcmp((L + i)->data.name, "0")) && (L + i)->data.price == 0) {break;}}for (j = 0; j < i; j++) {printf("%s %s %.2f\n", (L + j)->data.no, (L + j)->data.name, (L + j)->data.price);}
}

实验中遇到的问题 ①

比较容易地写出来了,但是有一个问题,无论是输入还是输出,都是在主函数中用的 L + i 这种类似数组特性的形式,L = L -> next; 这一类的能不能尝试使用呢?
如下图,在运行时出错。

运行时出错的代码:

#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
#include<iostream>
using namespace std;
#define MAXSIZE 100
#define OK 1
#define ERROR 0
typedef int Status;typedef struct {char no[20];               //图书ISBNchar name[50];             //图书名字float price;               //图书价格
}Book;
typedef Book Elemtype;//————————————单链表的存储结构————————————
typedef struct LNode {Elemtype data;//结点的数据域struct LNode* next;//结点的指针域
}LNode, * LinkList;//LinkList为指向结构体LNode的指针类型
//构造一个空的单链表
Status InitList(LinkList& L) {L = (LNode*)malloc(sizeof(LNode));//生成新结点作为头结点,用头指针L指向头结点//L = (LinkList)malloc(sizeof(LNode));//这种写法也可以L->next = NULL;return OK;
}
int main() {int i, j;LNode* L = NULL;InitList(L);LinkList p = L;for (i = 0; i < MAXSIZE; i++) {scanf("%s %s %f", &L->data.no, &L->data.name, &L->data.price);if (!(strcmp(L->data.no, "0")) && !(strcmp(L->data.name, "0")) && L->data.price == 0) {break;}L = L->next;}for (j = 0; j < i; j++) {printf("%s %s %.2f\n", p->data.no, p->data.name, p->data.price);p = p->next;}
}

首先,上述代码有很严重的问题。

问题在于这里直接输入值,根本没有创建链表的过程。p = p -> next;这句硬生生写进去的,是错误的根源。没有用到前插法和后插法,或者说根本没有用到链表的特性,根本没有创建链表的这一过程。
p = p -> next;之前p -> next = NULL;但是p -> next -> next根本没有值,直接就是运行错误了。
修改后的代码,采用了后插法。

#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
#include<iostream>
using namespace std;
#define MAXSIZE 100
#define OK 1
#define ERROR 0
typedef int Status;
int i;
typedef struct {char no[20];               //图书ISBNchar name[50];             //图书名字float price;               //图书价格
}Book;
typedef Book Elemtype;//————————————单链表的存储结构————————————
typedef struct LNode {Elemtype data;//结点的数据域struct LNode* next;//结点的指针域
}LNode, * LinkList;//LinkList为指向结构体LNode的指针类型
//构造一个空的单链表
Status InitList(LinkList& L) {//L = (LNode*)malloc(sizeof(LNode));//生成新结点作为头结点,用头指针L指向头结点//L = (LinkList)malloc(sizeof(LNode));//这种写法也可以L = new LNode;L->next = NULL;return OK;
}//单链表的取值
/*
Status GetElem(LinkList L, int i, Elemtype& e) {//在带头结点的单链表L中根据序号i获得元素的值,用e返回L中第i个数据元素的值LNode* p = L->next;  //初始化,p指向首元结点int j = 1;   //计数器j初值赋为1while (p && j < i)//顺链域向后扫描,直到p为空或者p指向第i个元素{p = p->next;//p指向下一个结点++j;  //计数器j相应加1}if (!p || j > i) return ERROR;  //i值不合法i>n或i<=0e = p->data;   //取第i个结点的数据域return OK;
}
*///下面这个纯粹是自创的,没有用到单链表的性质,倒是用到了数组的性质
/*
int main() {int i, j;LNode* L = NULL;InitList(L);for (i = 0; i < MAXSIZE; i++) {scanf("%s %s %f", &(L + i)->data.no, &(L + i)->data.name, &(L + i)->data.price);if (!(strcmp((L + i)->data.no, "0")) && !(strcmp((L + i)->data.name, "0")) && (L + i)->data.price == 0) {break;}}for (j = 0; j < i; j++) {printf("%s %s %.2f\n", (L + j)->data.no, (L + j)->data.name, (L + j)->data.price);}
}*///前插法,顺序不对
/*
void CreateList_H(LinkList& L) {//逆序输入n个元素的值,建立带表头结点的单链表LL = new LNode;L->next = NULL;for (i = 0; i < MAXSIZE; i++) {LinkList p = new LNode;scanf("%s %s %f", &p->data.no, &p->data.name, &p->data.price);p->next = L->next;L->next = p;if (!(strcmp(p->data.no, "0")) && !(strcmp(p->data.name, "0")) && p->data.price == 0) {break;}}L->next = NULL;
}*///后插法,才是题目所要的次序
void CreateList_H(LinkList& L) {//正位序输入n个元素的值,建立带表头结点的单链表LL = new LNode;L->next = NULL;  //先建立一个带头结点的空链表LinkList r = L;           //尾指针r指向头结点for (i = 0; i < MAXSIZE; i++) {LinkList p = new LNode;scanf("%s %s %f", &p->data.no, &p->data.name, &p->data.price);if (!(strcmp(p->data.no, "0")) && !(strcmp(p->data.name, "0")) && p->data.price == 0) {break;}p->next = NULL;r->next = p;r = p;}
}int main() {int  j;LNode* L;InitList(L);CreateList_H(L);for (j = 0; j < i; j++) {L = L->next;printf("%s %s %.2f\n", L->data.no, L->data.name, L->data.price);}
}

实验中遇到的问题 ②

关于空指针的问题。

编程这样的空指针错误怎么改(C语言)?
char *str=null;strcpy(str,“hello world”);
事先不知道要存入的字符串长度
最佳答案
2008-11-22 回答char * str=(char * )malloc(11 * sizeof(char));strcpy(str,“hello world”);
明镜止水7级
2008-11-22 回答可以动态分配内存,把容量设置的大一点就行了。
北啊,您在哪2级
2008-11-22 回答用memcpy()不用担心溢出
嘬近dè地方5级
2008-11-22 回答char *str=null;表明str是空指针,无实地址,使用前必须申请地址,不管是用strcpy还是memcpy,想存放字符,就得申请地址。可以用malloc等申请地址。
和谐4级
2008-12-05 回答用动态存储分配吧

实验中遇到的问题 ③

前插法后插法的差异。

前插法

void CreateList_H(LinkList& L) {//逆序输入n个元素的值,建立带表头结点的单链表LL = new LNode;L->next = NULL;for (i = 0; i < MAXSIZE; i++) {LinkList p = new LNode;scanf("%s %s %f", &p->data.no, &p->data.name, &p->data.price);p->next = L->next;L->next = p;if (!(strcmp(p->data.no, "0")) && !(strcmp(p->data.name, "0")) && p->data.price == 0) {break;}}L->next = NULL;
}

前插法需要逆序输入元素的值。

后插法

//后插法,才是题目所要的次序
void CreateList_H(LinkList& L) {//正位序输入n个元素的值,建立带表头结点的单链表LL = new LNode;L->next = NULL;  //先建立一个带头结点的空链表LinkList r = L;           //尾指针r指向头结点for (i = 0; i < MAXSIZE; i++) {LinkList p = new LNode;scanf("%s %s %f", &p->data.no, &p->data.name, &p->data.price);if (!(strcmp(p->data.no, "0")) && !(strcmp(p->data.name, "0")) && p->data.price == 0) {break;}p->next = NULL;r->next = p;r = p;}
}

后插法是正序输入,才符合题目要求。

15.基于链式存储结构的图书信息表的价格最贵图书的查找

问题描述
定义一个包含图书信息(书号、书名、价格)的链表,读入相应的图书数据来完成图书信息表的创建。然后,查找价格最高的图书,输出相应图书的信息。
输入要求
总计输入n+1行,其中,第一行是图书数目n,后n行是n本图书的信息(书号、书名、价格),每本图书信息占一行,书号、书名、价格用空格分隔,价格之后没有空格。其中,书号和书名为字符串类型,价格为浮点数类型。
输出要求
总计输出m+1行,其中,第一行是最贵图书数目(价格最高的图书可能有多本),后m行是最贵图书的信息(书号、书名、价格),每本图书信息占一行,书号、书名、价格用空格分隔。其中,价格输出保留两位小数。
输入样例
6
9787302257646 程序设计基础 25.00
9787302164340 程序设计基础(第2版) 20.00
9787302219972 单片机技术及应用 32.00
9787302203513 单片机原理与应用技术 26.00
9787810827430 工业计算机控制技术——原理与应用 29.00
9787811230710 C#程序设计易懂易会教程 32.00
输出样例
2
9787302219972 单片机技术及应用 32.00
9787811230710 C#程序设计易懂易会教程 32.00

代码

#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
#include<iostream>
using namespace std;
#define MAXSIZE 100
#define OK 1
#define ERROR 0
typedef int Status;
int i;
typedef struct
{char no[20];               //图书ISBNchar name[50];             //图书名字float price;               //图书价格
}Book;
typedef Book Elemtype;//————————————单链表的存储结构————————————
typedef struct LNode
{Elemtype data;//结点的数据域struct LNode* next;//结点的指针域
}LNode, * LinkList;//LinkList为指向结构体LNode的指针类型
//构造一个空的单链表
Status InitList(LinkList& L)
{//L = (LNode*)malloc(sizeof(LNode));//生成新结点作为头结点,用头指针L指向头结点//L = (LinkList)malloc(sizeof(LNode));//这种写法也可以L = new LNode;L->next = NULL;return OK;
}
LinkList r_;
LinkList r_1;//后插法,才是题目所要的次序
void CreateList_H(LinkList& L, int n)
{//正位序输入n个元素的值,建立带表头结点的单链表LL = new LNode;L->next = NULL;  //先建立一个带头结点的空链表LinkList r = L;           //尾指针r指向头结点for (i = 0; i < n; i++){LinkList p = new LNode;scanf("%s %s %f", &p->data.no, &p->data.name, &p->data.price);p->next = NULL;r->next = p;r = p;}
}
//sort交换数据域,取最大值
LinkList GetElem_max(LinkList& L)
{//链表共有i个结点LNode* p = L->next;  //初始化,p指向首元结点Book temp;for (int j = 0; j < i - 1; j++){for (int k = 0; k < i - 1 - j; k++){if (p->data.price > p->next->data.price){//只需要交换数据域temp = p->next->data;p->next->data = p->data;p->data = temp;}p = p->next;if (j == 0){r_ = p;}}p = L->next;r_1 = p;}LinkList e = p;return e;
}
int main()
{int n;//图书书目int count_ = 1;scanf("%d", &n);LNode* L;InitList(L);CreateList_H(L, n);LinkList m = GetElem_max(L);LinkList r_3 = r_1;for (int i_2 = 0; i_2 < i - 1; i_2++){if (r_1->data.price == r_->data.price){count_++;}r_1 = r_1->next;}printf("%d\n", count_);for (int i_3 = 0; i_3 < i - count_; i_3++){r_3 = r_3->next;}for (int i_4 = 0; i_4 < count_; i_4++){printf("%s %s %.2f\n", r_3->data.no, r_3->data.name, r_3->data.price);while (r_3 != r_){r_3 = r_3->next;}}
}

实验中遇到的问题 ①

第一个需要注意的要点就是,不能只用一层冒泡排序,因为它不只是把最左边的最大值输出,它得把所有的价格都是最大的输出,也就是说,不能排除最大值不止一个的情况。
这里采取的方法就是用冒泡排序,然后额外设置了两个指针,一个指向头结点,一个指向尾部最大值的那个结点,然后再遍历链表,当往next方向移动的结点的数据域对应的价格等于最大时,count自加(count从1开始),就可以得到累加后的count值,再把第一次相等时候的链表中结点的位置记下来,用一个for循环,把后面的逐个输出就完成了。

实验中遇到的问题 ②

第二个需要注意的是这里冒泡排序的循环不需要交换结点那么复杂,只需要交换结点所对应的数据域就行了。

if (p->data.price > p->next->data.price){//只需要交换数据域temp = p->next->data;p->next->data = p->data;p->data = temp;}

19.基于链式存储结构的图书信息表的旧图书的出库

问题描述
定义一个包含图书信息(书号、书名、价格)的链表,读入相应的图书数据来完成图书信息表的创建。然后,根据指定的待出库的旧图书的位置,将该图书从图书表中删除。最后,输出该图书出库后的所有图书的信息。
输入要求
总计n+2行。首先输入n+1行,其中,第一行是图书数目n,后n行是n本图书的信息(书号、书名、价格),每本图书信息占一行,书号、书名、价格用空格分隔,价格之后没有空格。其中书号和书名为字符串类型,价格为浮点数类型。之后输入第n+2行,内容仅为一个整数,代表待删除的旧图书的位置序号。
输出要求
若删除成功:
输出旧图书出库后所有图书的信息(书号、书名、价格),总计n-1行,每行是一本图书的信息,书号、书名、价格用空格分隔。其中,价格输出保留两位小数。
若删除失败:
只输出以下一行提示:抱歉,出库位置非法!
输入样例
6
9787302257646 程序设计基础 25.00
9787302164340 程序设计基础(第2版) 20.00
9787302219972 单片机技术及应用 32.00
9787302203513 单片机原理与应用技术 26.00
9787810827430 工业计算机控制技术——原理与应用 29.00
9787811234923 汇编语言程序设计教程 21.00
2
输出样例
9787302257646 程序设计基础 25.00
9787302219972 单片机技术及应用 32.00
9787302203513 单片机原理与应用技术 26.00
9787810827430 工业计算机控制技术——原理与应用 29.00
9787811234923 汇编语言程序设计教程 21.00

代码

#define _CRT_SECURE_NO_DEPRECATE
#include<stdio.h>
#include<iostream>
using namespace std;
#define MAXSIZE 100
#define OK 1
#define ERROR 0
typedef int Status;
int i;
typedef struct
{char no[20];               //图书ISBNchar name[50];             //图书名字float price;               //图书价格
}Book;
typedef Book Elemtype;//————————————单链表的存储结构————————————
typedef struct LNode
{Elemtype data;//结点的数据域struct LNode* next;//结点的指针域
}LNode, * LinkList;//LinkList为指向结构体LNode的指针类型
//构造一个空的单链表
Status InitList(LinkList& L)
{//L = (LNode*)malloc(sizeof(LNode));//生成新结点作为头结点,用头指针L指向头结点//L = (LinkList)malloc(sizeof(LNode));//这种写法也可以L = new LNode;L->next = NULL;return OK;
}
//创建单链表
void CreateList_H(LinkList& L, int n)
{//正位序输入n个元素的值,建立带表头结点的单链表LL = new LNode;L->next = NULL;  //先建立一个带头结点的空链表LinkList r = L;           //尾指针r指向头结点for (i = 0; i < n; i++){LinkList p = new LNode;scanf("%s %s %f", &p->data.no, &p->data.name, &p->data.price);p->next = NULL;r->next = p;r = p;}
}//单链表的删除
Status ListDelete(LinkList& L, int i)
{//在带头结点的单链表中,删除第i个元素LinkList p = L;int j = 0;LinkList q;while ((p->next) && (j < i - 1))  //查找第i-1个结点,p指向该结点{p = p->next;++j;}//if (!(p->next) || (j > i - 1)) return ERROR;   //当i>n或i<1时,删除位置不合理if (!(p->next) || (j > i - 1)){printf("抱歉,出库位置非法!");return ERROR;}q = p->next;     //临时保存被删结点的地址以备释放p->next = q->next;  //改变删除结点前驱结点的指针域delete q;  //释放删除结点的空间return OK;
}
/*
删除算法中的循环条件(p->next&&j<i-1)和插入算法中的循环条件(p&&(j<i-1))是有所区别的。
因为插入操作中合法的插入位置有n+1个,而删除操作中合法的删除位置只有n个,
如果使用与插入操作相同的循环条件,则会出现引用空指针的情况,使删除操作失败。
空指针会失败!!!
*/int main()
{int n;//图书书目scanf("%d", &n);LNode* L;InitList(L);CreateList_H(L, n);int m;scanf("%d", &m);ListDelete(L, m);for (int m_1 = 0; m_1 < i - m + 1; m_1++){printf("%s %s %.2f\n", L->next->data.no, L->next->data.name, L->next->data.price);L = L->next;}
}

实验中遇到的问题 ①

删除算法中的循环条件(p->next&&j<i-1)和插入算法中的循环条件(p&&(j<i-1))是有所区别的。
因为插入操作中合法的插入位置有n+1个,而删除操作中合法的删除位置只有n个,
如果使用与插入操作相同的循环条件,则会出现引用空指针的情况,使删除操作失败。
空指针会失败!!!

这是严蔚敏数据结构书上的一句话,引用空指针的情况,使得操作失败,是我前面多次踩的坑了,引用空指针的时候,写代码不会报错,但是运行会出错/无输出退出,单步调试(逐过程)会卡在某一步。但就删除算法本身而言,这个实验还是比较简单的。

实验中遇到的问题 ②

循环究竟要循环多少次的问题,循环次数不对,很多时候并不会报错,但是在运行时候会无输出退出,单步调试会卡住,或者输出“屯屯屯屯屯”之类的乱码。

个人小结

从开始决定写,到现在完成陆陆续续有好几天了,也算是填了大一的坑了吧。
感觉C语言的应用这方面有了十足的进步,不过代码规范等方面,还是没有兼顾到,期待下一次的蜕变。

数据结构 基于线性表的图书信息管理相关推荐

  1. 数据结构实验--基于线性表的图书信息管理系统

    本文是依据数据结构习题解析与实验指导(李冬梅)一书中的第一个实验–基于线性表的图书信息管理系统所写的. 之所以写这个,是因为这个实验不仅涉及到线性表的结构设计,还包括一些线性表的基本操作,个人认为,做 ...

  2. 数据结构实验1《基于线性表的图书管理系统》

    数据结构实验1<基于线性表的图书管理系统> (visual studio 2019可运行) 输入及输出要求见<数据结构C语言(第二版)>严蔚敏版 [本文仅用于啥都看不懂还想交作 ...

  3. 基于线性表的图书信息管理系统

    基于线性表的图书信息管理系统 [实验目的] 1.掌握线性表的顺序存储表示和链式存储表示. 2.掌握顺序表和链表的基本操作,包括创建.查找.插入和删除等算法. 3.明确线性表两种不同存储结构的特点及其适 ...

  4. 数据结构之线性表----一文看懂顺序表、单链表、双链表、循环链表

    ​ 线性表是数据结构中比较基础的内容,不过也是入门的所需要客服的第一个难关.因为从这里开始,就需要我们动手编程,这就对很多同学的动手能力提出了挑战.不过这些都是我们需要克服的阵痛,学习新的知识总是痛苦 ...

  5. Java数据结构(1.1):数据结构入门+线性表、算法时间复杂度与空间复杂度、线性表、顺序表、单双链表实现、Java线性表、栈、队列、Java栈与队列。

    数据结构与算法入门 问题1:为什么要学习数据结构          如果说学习语文的最终目的是写小说的话,那么能不能在识字.组词.造句后就直接写小说了,肯定是不行的, 中间还有一个必经的阶段:就是写作 ...

  6. python线性表和队列_[笔记]python数据结构之线性表:linkedlist链表,stack栈,queue队列...

    python数据结构之线性表 python内置了很多高级数据结构,list,dict,tuple,string,set等,在使用的时候十分舒心.但是,如果从一个初学者的角度利用python学习数据结构 ...

  7. c语言用两个栈构造队列伪码,数据结构习题线性表栈队列.doc

    数据结构习题线性表栈队列 线性表(58) 1. 在单链表.双链表和单循环链表中,若仅知道指针p指向某结点,不知道头指针,能否将结点*p从相应的链表中删去?若可以,其时间复杂度各为多少? 2.设线性表的 ...

  8. 【Java数据结构】线性表

    线性表 线性表是最基本.最简单.也是最常用的一种数据结构. 线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的(注意,这句话只适用大部分线性表,而 ...

  9. step3 . day2 数据结构之线性表链表

    今天继续学习数据结构的线性表部分,从基础的顺序表到链表,类比写了一些调用函数,完成了表的增删改查排序等问题. 尤其是链表的排序,费了很大的心思终于捋顺写出来了,小有成就感,而且代码一次通过率越来越高, ...

最新文章

  1. shell date 格式化
  2. C# 创建Word项目标号列表、多级编号列表
  3. Java进阶:ReentrantLock和Condition基本使用
  4. ELK logstash 配置自定义字段为索引
  5. Android自定义属性 format详解
  6. 在Bootstrap开发框架中使用bootstrap-datepicker插件
  7. 一元三次方程重根判别式_如何求一元三次方程
  8. (软件工程复习核心重点)第四章总体设计-第二节:设计原理
  9. Codeforces 384E-线段树+dfs序
  10. 6月14日Linux设备驱动开发免费讲座PPT
  11. 开培训会没人来,是正常的
  12. java.lang.UnsatifiedLinkError错误一例:在eclipse中启动应用报错
  13. L3G400d单独使用实验
  14. Spring:Spring支持的bean作用域有哪些
  15. 帝国CMS系统自动生成sitemap.xml网站地图的教程
  16. 新能源车辆越来越多了,车牌识别核心技术
  17. 51Nod【1419】-最小公倍数挑战
  18. set_xscale 表示x轴缩放比例,一张图明明白白
  19. 网上企业订货系统平台源码价介绍|移讯云手机订单管理软件
  20. 什么是真值(truthy)

热门文章

  1. Verilog HDL 学习篇——六位数码管驱动
  2. UDP打洞(UDP Hole Punching)原理
  3. 宣传活动任务后台管理之促销活动业务分析...
  4. 2022年陕西省中级工程师职称评审流程及申报条件
  5. solidity 中的时间_Solidity快速了解
  6. 5.2.4 js循环小练习02 6 做学院评奖系统​ 如果数学成绩大于80分并且语文成绩大于80分,获奖学金500元。​如果数学小于30并且语文小于30分,输出重修。 两个数a、b,如果a能被b整除
  7. android 仿360浮动,Android仿360悬浮小球自定义view实现示例
  8. Unity中游戏存档方式
  9. BZOJ2095[Poi2010] Bridges
  10. Leetcode95. 不同的二叉搜索树 II(C语言)