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

实验前的准备

IDE的选择

采用的IDE是Visual_Studio_2019,这里需要关掉SDL检查
vs2019 关闭sdl检查的过程
关闭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];               //图书ISBN
	char 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+1
	if ((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;//表长+1
	return 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];               //图书ISBN
	char 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];               //图书ISBN
	char 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];               //图书ISBN
	char 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];               //图书ISBN
	char 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+1
	if ((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;//表长加1
	return 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];               //图书ISBN
	char 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个元素的值,建立带表头结点的单链表L
	L = 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];               //图书ISBN
	char 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];               //图书ISBN
	char 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];               //图书ISBN
	char 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初值赋为1
	while (p && j < i)//顺链域向后扫描,直到p为空或者p指向第i个元素
	{
		p = p->next;//p指向下一个结点
		++j;  //计数器j相应加1
	}
	if (!p || j > i) return ERROR;  //i值不合法i>n或i<=0
	e = 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个元素的值,建立带表头结点的单链表L
	L = 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个元素的值,建立带表头结点的单链表L
	L = 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个元素的值,建立带表头结点的单链表L
	L = 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个元素的值,建立带表头结点的单链表L
	L = 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];               //图书ISBN
	char 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个元素的值,建立带表头结点的单链表L
	L = 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];               //图书ISBN
	char 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个元素的值,建立带表头结点的单链表L
	L = 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语言的应用这方面有了十足的进步,不过代码规范等方面,还是没有兼顾到,期待下一次的蜕变。

  • 47
    点赞
  • 213
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值