数据结构课程设计预习报告

数据结构课程设计

任务一:图书信息管理系统的设计与实现

(一)实验内容:

设计并实现一个图书信息管理系统。根据实验要求设计该系统的菜单和交互逻辑,并编码实现增删改查的各项功能。 该系统至少包含以下功能:
(1)根据指定图书个数,逐个输入图书信息;
(2)逐个显示图书表中所有图书的相关信息;
(3)能根据指定的待入库的新图书的位置和信息,将新图书插入到图书表中指定的位置;
(4)根据指定的待出库的旧图书的位置,将该图书从图书表中删除;
(5)能统计表中图书个数;
(6)实现图书信息表的图书去重;
(7)实现最爱书籍查询,根据书名进行折半查找,要求使用非递归算法实现,成功返回此书籍的书号和价格;
(8)图书信息表按指定条件进行批量修改;
(9)利用快速排序按照图书价格降序排序;
(10)实现最贵图书的查找;

(二)实验提示:

图书信息的定义:

typedef struct {
    char no[8];   //8位书号
    char name[20]; //书名
    int price;     //价格
}Book;

顺序表的定义:

typedef  struct {
  Book  *elem;     //指向数据元素的基地址
  int  length;       //线性表的当前长度                                                           
 }SqList;

链表的定义:

typedef struct LNode{
     Book   data;       //数据域
     struct LNode  *next;   //指针域
}LNode,*LinkList;  
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 汇编语言程序设计教程 32.00
0 0 0

2、基于顺序存储/链式存储结构的图书信息表的修改

读入图书信息表,然后计算所有图书的平均价格,将所有低于平均价格的图书价格提高20%,所有高于或等于平均价格的图书价格提高10%,最后逐行输出价格修改后的图书信息。

输入

输入 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 操作系统教程 32.00
0 0 0

输出样例

9787302257646 程序设计基础 30.00
9787302164340 程序设计基础(第 2 版) 24.00
9787302219972 数据挖掘与机器学习 35.20
9787302203513 模式识别与智能计算 28.60
9787810827430 工业计算机控制技术——原理与应用 31.90
9787811234923 操作系统教程 35.20

3、基于顺序存储/链式存储结构的图书信息表的最贵图书查找

读入相应的图书信息表,然后查找价格最高的图书,输出相应图书的信息。

输出

总计 m+1 行,其中,第 1 行是最贵的图书数目,(价格最高的图书可能有多本),后m行是最贵图书的信息,每本图书信息占一行,书号、书名、价格用空格分隔。其中价格输出保留两位小数。

输出样例

2
9787302219972 数据挖掘与机器学习 35.20
9787811234923 操作系统教程 35.20

4、基于顺序存储/链式存储结构的图书信息表的最爱图书的查找

读入相应的图书信息表,然后根据指定的最爱图书的名字,输出相应图书的信息。

输入

输入1行,为每次待查找的最爱图书名字。

输出

若查找成功,输出k+1行,对于每次查找,第一行是最爱图书数目,同一书名的图书可能有多本,后K行是最爱图书的信息(书号、书名、价格),每本图书信息占一行,书号、书名、价格用空格分隔,其中价格输出保留两位小数。若查找失败:只输出以下提示:抱歉,没有你的最爱!

输出样例

2
9787302257646 程序设计基础 30.00
9787302164340 程序设计基础(第 2 版) 24.00

5、基于顺序存储/链式存储结构的图书信息表的新书入库

读入指定的待入库的新图书的位置和信息,将新图书插入到图书表中指定的位置上,最后输出新图书入库后所有图书的信息。

输入

总计n+1行,首先输入第1行,内容仅为一个整数,代表待入库的新图书的位置序号,然后输入n行,内容为新图书的信息,书号、书名、价格用空格分隔。

输出

若插入成功,输出新图书入库后所有图书的信息(书号、书名、价格),总计n+1行,每行是一本图书的信息,书号、书名、价格用空格分隔。其中价格输出保留两位小数。
若插入失败,只输出以下提示:抱歉,入库位置非法!

输入样例

2
9787302265436 计算机导论实验指导 18.00

输出样例

9787302257646 程序设计基础 30.00
9787302265436 计算机导论实验指导 18.00
9787302164340 程序设计基础(第 2 版) 24.00
9787302219972 数据挖掘与机器学习 35.20
9787302203513 模式识别与智能计算 28.60
9787810827430 工业计算机控制技术——原理与应用 31.90
9787811234923 操作系统教程 35.20

6、基于顺序存储/链式存储结构的图书信息表的旧书出库

读入指定的待出库的旧图书的书号,将该图书从图书表中删除,最后输出旧图书出库后所有图书的信息。

输入

输入待出库的旧图书的书号;

输出

若删除成功,输出旧图书出库后所有图书的信息(书号、书名、价格),每行是一本图书的信息,书号、书名、价格用空格分隔。其中价格输出保留两位小数。
若删除失败,只输出以下提示:出库失败,未找到该图书!

7、基于顺序存储/链式存储结构的图书信息表的图书去重

出版社出版的任何一本图书的书号(ISBN)都是唯一的,即图书表中不允许包含书号重复的图书。读入相应的图书信息表(事先加入书号重复的记录),然后进行图书的去重,即删除书号重复的图书(只留第一本),最后输出去重后所有图书的信息。

输出

总计输出m+1行(m<=n),其中,第一行是去重后的图书数目,后m行是去重后图书的信息(书号、书名、价格),每本图书信息占一行,书号、书名、价格用空格分隔,其中价格输出保留两位小数。

(三)实现思路:

第一个任务实现比较简单,只需要分模块地构建函数,再用条件选择语句分模块供用户实现功能,即可实现图书信息管理系统。

(四)实现代码:

#include <iostream>
#include <cstring>//将一些隐藏变量编入命名空间,修正一些C++编译器认为Bug的代码
#include <iomanip>//保留两位小数需要使用
using namespace std;

//图书信息的结构体
typedef struct {
	char no[15]; //15位书号
	char name[40]; //书名
	double price; //价格
}Book;

//定义数据结构——链表,由于图书信息经常需要查找,在顺序表和链表中,链表更适合存储经常需要查找的数据
typedef struct LNode {
	Book data; //数据域
	struct LNode* next; //指针域
}LNode, * LinkList;

LNode* first; //定义链表头结点

//逐个显示图书表中所有图书的相关信息
void BookOut() {
	cout << "管理系统中全部图书信息如下:" << endl;
	LNode* p = first->next;
	while (p) {
		cout << p->data.no << ' ' << p->data.name << ' ';
		cout << setiosflags(ios::fixed) << setprecision(2) << p->data.price << endl;//保留两位小数
	    p = p->next;
	}
}

//根据指定的待入库的新图书的位置和信息,将新图书插入到图书表中指定的位置
void BookInsert(int i,Book book) {
	LNode* p = first;
	int j = 0;
	//寻找插入位置的前一个位置
	while (p && j < i - 1) { p = p->next; j++; }
	if (!p) cout << "位置出错" << endl;
	else {
		LNode* s = new LNode();
		s->data = book;
		s->next = p->next;
		p->next = s;
	}
}

//根据指定的待出库的旧图书的位置,将该图书从图书表中删除
void BookDelete(int i) {
	LNode* p = first;
	int j = 0;
	while (p && j < i - 1) { p = p->next; j++; }
	if (!p || !p->next)cout << "位置出错" << endl;
	else {
		LNode* q = p->next;
		Book book = q->data;
		p->next = q->next;
		cout << "删除的图书信息为:" << q->data.no << q->data.name << setiosflags(ios::fixed) << setprecision(2) << q->data.price << endl;
		delete q;
	}
}

//统计表中图书个数
int BookCount() {
	LNode* p = first->next;
	int count = 0;
	while (p) {
		count++;
		p = p->next;
	}
	return count;
}

//图书信息表的图书去重
void BookNosame() {
	LNode* p = first->next, * r = p->next, * l = first;
	while (p) {
		r = p->next;
		while (r) {
			if (strcmp(p->data.no, r->data.no) == 0) {
				l->next = p;
				l->next = p->next;
				break;
			}
			r = r->next;
		}
		p = p->next;
	}
}

/*
//二叉排序树
typedef int KeyType;
struct RecType { KeyType key; };
struct BiNode {
	KeyType key;
	BiNode* lchild, * rchild;
};
BiNode* InsertBST(BiNode* root, BiNode* s) {
	BiNode* p, * q;
	if (root == NULL)return s;
	else {
		q = NULL; p = root;
		while (p != NULL) {
			q = p;
			if (s->key < p->key)p = p->lchild;
			else p = p->rchild;
		}
		if (s->key < q->key)q->lchild = s;
		else q->rchild = s;
		return root;
	}
}
void BisortTree(RecType a[], int n) {
	BiNode* root;
	root = NULL;
	for (int i = 0; i < n; i++){
		BiNode* s = new BiNode;
		s->key = a[i].key;
		s->lchild = NULL;
		s->rchild = NULL;
		root = InsertBST(root, s);
	}
}*/

//顺序查找喜爱图书
void BookFind(char name[]) {
	LNode* p = first->next;
	Book book[50];
	int i = 0;
	while (p) {
		if (strcmp(p->data.name, name) == 0) {
			book[i] = p->data;
			i++;
		}
		p = p->next;
	}
	if (i == 0) { cout << "抱歉,没有你的最爱!" << endl; }
	else { 
		cout << "查询到的图书数量为:" << i << endl << "图书信息如下:" << endl;
		for (int j = 0; j < i; j++) {
			cout << book[j].no << ' ' << book[j].name << ' ' << book[j].price << endl;
		}
	}
}

//图书信息表按低于均价提升20%,高于均价提升10%进行批量修改
void BookChange() {
	LNode* p = first->next;
	double average = 0;
	while (p) {
		average += p->data.price;
		p = p->next;
	}
	average /= BookCount();
	p = first->next;//重置p指针
	while (p) {
		if (p->data.price < average) {
			p->data.price *= 1.2;
		}
		else { p->data.price *= 1.1; }
		p = p->next;
	}
}

/*利用直接插入排序按照图书价格降序排序
void BookSort() {
	LNode* p = first->next, * r = p->next, * l = p;
	while (r) {
		Book temp = r->data;
		l->next = r;
		while (l && l->data.price < temp.price) {
			l->next->data = l->data;
			l->next = l;
		}
		if (l->next != r) { l->next->data = temp; }
		r = r->next;
	}
}*/
//利用冒泡排序按照图书价格降序排序
void BookSort() {
	LNode* p = first->next, * r = p;
	while (p->next->next != NULL) {
		while (r->next != NULL) {
			if (r->data.price < r->next->data.price) {
				Book temp = r->data;
				r->data = r->next->data;
				r->next->data = temp;
			}
			r = r->next;
		}
		p = p->next;
	}
}

//最贵图书的查找
void BookExpensive() {
	LNode* p = first->next;
	double max = 0;
	while (p) {
		if (p->data.price > max) { max = p->data.price; }
		p = p->next;
	}
	cout << "最贵图书如下:" << endl;
	p = first->next;//重置p指针
	while (p) {
		if (p->data.price == max) { cout << p->data.no << ' ' << p->data.name << ' ' << setiosflags(ios::fixed) << setprecision(2) << p->data.price << endl; }
		p = p->next;
	}
}

//用户使用界面菜单
void Menu() {
	cout << "~~欢迎使用图书信息管理系统~~" << endl;
	cout << "~请输入对应序号使用对应功能~" << endl;
	cout << "~~~~~~1.输出所有图书~~~~~~" << endl;
	cout << "~~~~~~2.插入一本图书~~~~~~" << endl;
	cout << "~~~~~~3.删除一本图书~~~~~~" << endl;
	cout << "~~~~~~4.查看图书总数~~~~~~" << endl;
	cout << "~~~~~~5.删除相同图书~~~~~~" << endl;
	cout << "~~~~~~6.寻找喜爱图书~~~~~~" << endl;
	cout << "~~~~~~7.提升图书价格~~~~~~" << endl;
	cout << "~~~~~~8.按照价格排序~~~~~~" << endl;
	cout << "~~~~~~9.查找最贵图书~~~~~~" << endl;
	cout << "~~~~~~~10.退出系统~~~~~~~" << endl;
}

int main() {
	Book book[50];//定义图书存储空间
	int count = 0, n;
	bool tag = true;
	while (tag) {
		cout << "请输入初始书本数量(0~50):" << endl;
		cin >> n;//初始书本数量
		if (n >= 0 && n <= 50) {
			while (count < n) {
				cout << "请输入书号,书名和价格(中间以空格符隔开)" << endl;
				cin >> book[count].no;
				cin >> book[count].name;
				cin >> book[count].price;
				count++;
			}
			tag = false;
		}
		else { cout << "输入不正确" << endl; }
	}
	//构造后接方式的单链表
	first = new LNode();
	first->next = NULL;
	LNode* r = first, * s;
	for (int i = 0; i < count; i++) { //r为尾指针
		s = new LNode();
		s->data = book[i];
		s->next = NULL;
		r->next = s;
		r = s;
	}
	r->next = NULL;
	int cho = 0;
	while (cho != 10) {
		Menu();
		cin >> cho;
		switch (cho) {
		case 1: {
			BookOut();
			break;
		}
		case 2: {
			cout << "请输入要插入的位置和图书的三个信息(中间用空格隔开):" << endl;
			int i;//插入位置
			cin >> i >> book[count].no >> book[count].name >> book[count].price;
			BookInsert(i, book[count]);
			break;
		}
		case 3: {
			int i;//删除位置
			cin >> i;
			BookDelete(i);
			break;
		}
		case 4: {
			cout << "图书管理系统中总共有:" << BookCount() << "本书。" << endl;
			break;
		}
		case 5: {
			BookNosame();
			break;
		}
		case 6: {
			char name[40];
			cin >> name;
			BookFind(name);
			break;
		}
		case 7: {
			BookChange();
			break;
		}
		case 8: {
			BookSort();
			break;
		}
		case 9: {
			BookExpensive();
			break;
		}
		}
	}
}

任务二:隐式图的搜索问题

(一)实验内容:

编写九宫重排问题的启发式搜索(A*算法)求解程序。
在3х3组成的九宫棋盘上,放置数码为1~8的8个棋子,棋盘中留有一个空格,空格周围的棋子可以移动到空格中,从而改变棋盘的布局。根据给定初始布局和目标布局,编程给出一个最优的走法序列。输出每个状态的棋盘
测试数据:初始状态:123456780 目标状态:012345678
【输入格式】
输入包含三行,每行3各整数,分别为0-8这九个数字,以空格符号隔开,标识问题的初始状态。0表示空格,例如:

2 0 3
1 8 4
7 6 5

在这里插入图片描述

(二)实验提示:

1、存储结构的定义
typedef struct node//八数码结构体
{
int nine[N][N];//数码状态
int f;//估价值
int direct;//空格移动方向
struct node *parent;//父节点
}pNode;
2、启发式搜索算法的描述

(1)把初始节点S0放入Open表中,f(S0)=g(S0)+h(S0);
(2)如果Open表为空,则问题无解,失败退出;
(3)把Open表的第一个节点取出放入Closed表,并记该节点为n;
(4)考察节点n是否为目标节点。若是,则找到了问题的解,成功退出;
(5)若节点n不可扩展,则转到第(2)步;
(6)扩展节点n,生成子节点ni(i=1,2,……),计算每一个子节点的估价值f(ni) (i=1,2,……),并为每一个子节点设置指向父节点的指针,然后将这些子节点放入Open表中;
(7)根据各节点的估价函数值,对Open表中的全部节点按从小到大的顺序重新进行排序;
(8)转到第(2)步。

启发式搜索算法的工作过程

在这里插入图片描述

(三)实现思路:

1.利用预估代价和当前代价和进行代价估算,以A算法进行求解。
2.输入初始状态和目标状态。
3.输出从初始状态到目标状态的路线。
首先八数码问题其实就是将数码移动到空白格,但是这样的思路会比较复杂,对于空白格不同的位置,能进行的移动表示起来非常不方便,需要分九种情况讨论。但是如果我们转换思路,变为空白格的移动,这样空白格始终可以在九格中进行上、下、左、右的移动,表示起来也非常简单了。
算法的思路便是每移动一次判断是否之前出现过这样的相同情况,如果有就去重,记录下所有的不重复的排列,找到最短路径。

(四)实现代码:

#include<iostream>
#include<queue>//队列,利用优先级队列
#include<stack>//栈,利用先进后出返回路径
#include<math.h>//使用abs()计算绝对值
using namespace std;

const int num = 9;//定义九宫数字常量9

struct Node {
	int square[9];  //将3×3的方针用一维数组来存储,方便表示,同时可以用逆序数判断是否存在路径
	struct Node* parent;//指向父节点的指针
	int value;//预估代价:当前状态每个数字与最终位置的距离。可以使用欧拉距离或者曼哈顿距离,本代码使用曼哈顿距离方便计算
	int depth;//当前代价:当前移动的步数
	//operator运算符重载函数一个参数调用operator <,另一个传值
	friend bool operator < (Node A, Node B) //根据value+depth的大小构造优先级队列
	{
		return A.value + A.depth > B.value + B.depth;//返回最小优先队列 
	}
};

priority_queue<Node> OPEN;     //构建优先级队列OPEN表,存放当前所有可选择路径
queue<Node> CLOSE;     //构建普通队列CLOSE表,存放每一步选择的代价最低的路径
stack<Node> Path;     //用栈存储最终选择的路径
int inversion1 = 0, inversion2 = 0; //初始状态和最终状态数组的逆序数

//利用逆序数判断初始状态是否能找到变为最终状态的路径
bool judge(Node& S, Node& G) {
	//初始化S、G状态
	S.parent = NULL;	S.depth = 0;	S.value = 0;
	G.parent = NULL;	G.depth = 0;	G.value = 0;
	cout << "请输入初始状态方阵:(数字0~8)" << endl;
	for (int i = 0; i < num; i++)//num为常量9
		cin >> S.square[i];
	cout << "请输入目标状态方阵:(数字0~8)" << endl;
	for (int i = 0; i < num; i++)
		cin >> G.square[i];
	// 利用线性代数知识,逆序数奇偶性一致则有解
	for (int i = 0; i < num - 1; i++)
		for (int j = i + 1; j < num; j++)
			if (S.square[i] > S.square[j] && S.square[i] * S.square[j] != 0)
				inversion1++;
	for (int i = 0; i < num - 1; i++)
		for (int j = i + 1; j < num; j++)
			if (G.square[i] > G.square[j] && G.square[i] * G.square[j] != 0)
				inversion2++;
	if (inversion1 % 2 == inversion2 % 2)
	{
		return true;
	}
	return false;
}

//计算代价值:每个数字到最终位置曼哈顿距离之和+当前代价的值
int cost(Node A, Node G)
{
	int count = 0, begin[3][3], end[3][3]; //count为曼哈顿距离
	for (int i = 0; i < num; i++) {
		//将定义的一维数组的值传给二维数组用于计算距离
		begin[i / 3][i % 3] = A.square[i];
		end[i / 3][i % 3] = G.square[i];
	}
	//循环嵌套遍历二维数组每一个元素
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		{
			if (begin[i][j] != end[i][j])//寻找每个位置数字不同的
			{
				//循环嵌套计算距离
				for (int m = 0; m < 3; m++)
					for (int n = 0; n < 3; n++)
						if (begin[i][j] == end[m][n])
							count += abs(i - m) + abs(j - n); //累加计算每个数字当前状态与最终状态的曼哈顿距离 
			}
		}
	return count + A.depth;//返回代价值
}

//判断两个方阵是否相等
bool equal(Node S, Node G)
{
	for (int i = 0; i < num; i++)
	{
		if (S.square[i] != G.square[i])
		{
			return false;
		}
	}
	return true;
}

//产生新的结点并存入OPEN表
void creatNode(Node& S, Node G)
{
	//判断空格位置,得出可移动的方向
	int blank; //空格0的位置
	for (blank = 0; blank < num; blank++) { if (S.square[blank] == 0)break; }//找到0的位置 
	int x = blank / 3, y = blank % 3; //计算0的行列
	//寻找新的可选结点,存入OPEN表
	for (int d = 0; d < 4; d++) 
	{
		int newX = x, newY = y;//定义新的0的行列,并且用当前位置赋初值
		Node tempNode;
		//定义四个方向
		if (d == 0)  newX = x - 1;  //向左
		if (d == 1)	 newY = y - 1;  //向下
		if (d == 2)  newX = x + 1;  //向右
		if (d == 3)	 newY = y + 1;  //向上
		int newblank = newX * 3 + newY; //新0的位置
		if (newX >= 0 && newX < 3 && newY >= 0 && newY < 3)//判断0的移动是否出界
		{
			//交换格子的值
			tempNode = S;
			tempNode.square[blank] = S.square[newblank];//坐标传值
			tempNode.square[newblank] = 0;//数值传值
			if (S.parent != NULL && (*S.parent).square[newblank] == 0) //舍弃新节点和父节点一样的结点
			{
				continue;
			}
			//把子节点都存入OPEN表
			tempNode.parent = &S;
			tempNode.value = cost(tempNode, G);//计算代价值
			tempNode.depth = S.depth + 1;
			OPEN.push(tempNode);
		}
	}
}

int main()
{
	//定义S0:初始状态、Sg:目标状态
	Node S0, Sg;
	if (!judge(S0, Sg))
	{
		cout << "没有任何一条路径可以到达!" << endl;
		return 0;
	}
	OPEN.push(S0);//将初始结点存入OPEN表中
	while (true)
	{
		CLOSE.push(OPEN.top()); //将OPEN表中优先级最高,即代价值最低的元素存入CLOSE表中
		OPEN.pop(); //删除OPEN表中优先级最高的元素
		if (!equal(CLOSE.back(), Sg)) //判断是否已经到目标状态,没有则拓展当前结点
		{
			creatNode(CLOSE.back(), Sg);
		}
		else
		{
			break;
		}
	}
	Node tempNode;//临时变量暂存队前数据
	tempNode = CLOSE.back();//返回CLOSE表最后入队,即最新入队的元素
	while (tempNode.parent != NULL)//将除了第一个元素全部依次压栈
	{
		Path.push(tempNode);//将最新一步入栈,形成栈底是最后一步,依次往上递减
		tempNode = *(tempNode.parent);//指向父节点的指针,从后向前遍历
	}
	Path.push(tempNode);//压入第一个元素
	cout << "至少要移动" << Path.size() - 1 << "步!" << endl;//由于有最初始状态也在栈中,所以栈大小-1就为步数
	cout << "******" << endl;//分割线,美观
	while (Path.size() != 0)
	{
		//打印当前步的九宫状态
		for (int i = 0; i < num; i++)
		{
			cout << Path.top().square[i] << " ";
			if ((i + 1) % 3 == 0)
				cout << endl;
		}
		Path.pop();//每一步逐个出栈
		cout << "******" << endl;//分割线,美观
	}
}

任务三:基于二叉排序树的低频词过滤系统

(一)实验内容:

1.对于一篇给定的英文文章,利用线性表和二叉排序树来实现单词频率的统计,实现低频词的过滤,并比较两种方法的效率。具体要求如下:
2.读取英文文章文件(Infile.txt),识别其中的单词。
3.分别利用线性表和二叉排序树构建单词的存储结构。当识别出一个单词后,若线性表或者二叉排序树中没有该单词,则在适当的位置上添加该单词;若该单词已经被识别,则增加其出现的频率。
4.统计结束后,删除出现频率低于五次的单词,并显示该单词和其出现频率。
5.其余单词及其出现频率按照从高到低的次序输出到文件中(Outfile.txt),同时输出用两种方法完成该工作所用的时间。
6.计算查找表的ASL值,分析比较两种方法的效率。
7.系统运行后主菜单如下:
在这里插入图片描述
当选择1后进入以下界面:
在这里插入图片描述
其中选择2时显示利用线性表来实现所有功能所用的时间。
当在主菜单选择2二叉排序树后,进入的界面与上图类同。

(二)实验提示:

1、在统计的过程中,分词时可以利用空格或者标点符号作为划分单词依据,文章中默认只包含英文单词和标点符号。
2、对单词进行排序时,是按照字母序进行的,每个结点还应包含该单词出现的频率。
3、存储结构的定义

  • 二叉排序树的存储表示
typedef struct BSTNode{
string  WordName;		//单词名称
   int count;			    //单词出现频率
struct BSTNode *next;
} BSTNode, *BSTree;

4、实现过程可参见教材上线性表和二叉排序树的相关算法。

(三)实现思路:

任务三和实验一的实现类似,首先需要分模块实现顺序表、二叉排序树的各种功能,对英语文章进行逐个字符的读取,利用合适的语句判定断点组成单词。

(四)实现代码:

#include<iostream>
using namespace std;
#include<string.h>//字符串比对,判断是否存在单词
#include<string>
#include<fstream>//读取txt文件
#include<vector>//向量
#include<ctime>//计时函数
#include <iomanip>//setw()设域宽
#define M 1000//最大单词存储容量

//单词结构体(拼写、频率)
typedef struct{
	char word[20];
	int count;
}danci;

//线性表的存储表示
typedef struct{
	danci* a;
	int length;
}SqList;

//初始化线性表
void InitList(SqList& L)
{
	L.a = new danci[M];//动态数组,无法预知有多少单词
	if (!L.a)
		cout << "存储空间分配失败!" << endl;
	else
		L.length = 0;//初始化表长
}

//判断单词是否已经出现
void Judge(SqList& L, danci n)
{
	int i;
	for (i = 0; i < L.length; i++)
	{
		if (strcmp(L.a[i].word, n.word) == 0)//已经存在
		{
			L.a[i].count++;//已经存在的单词频率+1
			break;
		}
	}
	if (L.length == 0 || i == L.length)//表中没有其他元素||判断到最后一个也不相等
	{
		L.length++;//给新的单词扩容
		int j = L.length;
		L.a[j] = n;//装入新单词
	}
}

//从txt中读取数据
void GetFromlist(SqList& L) {
	int K;
	FILE* fp;
	char ch;//存储单个字符的空间
	danci e;//中间临时变量
	fp = fopen("C://Infile.txt", "r");//打开Infile.txt文件
	while (!feof(fp))//判断是否读取到文件最后
	{
		ch = getc(fp);//获取当前字符
		if (!(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { continue; }
		else{//组成一个单词
			K = 0;//重置在当前单词中字母序号
			e.count = 1;//初始化当前单词频率
			while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch == 39) || ch == '-')//39为'
			{
				if (ch >= 'A' && ch <= 'Z')
					ch += 32;//大写转换成小写
				e.word[K++] = ch;//把当前字母存入当前单词数组,并给K自增
				ch = getc(fp);//读取直到字母,’,-以外的字符
			}
			e.word[K++] = '\0';//结束标志\0,防止经典烫烫烫乱码
		}
		Judge(L, e);//比较是否出现
	}
}

//显示单词和对应出现频率
void Show(SqList& L)
{
	cout << "出现次数大于五次的单词如下:" << endl;//因为会先打印所有<5的并删除
	cout << "单词" << setw(15) << "数量" << setw(15) << endl;
	for (int i = 0; i < L.length; i++)
	{
		cout << L.a[i].word << setw(15) << L.a[i].count << setw(15) << endl;
	}
}

//筛选出现次数小于5的单词
void Find5(SqList& L)
{
	int n = 1;
	cout << "出现次数小于五次的单词如下:" << endl;
	cout << "单词" << setw(15) << "数量" << setw(15) << endl;
	for (int i = L.length; i > 0; i--)
	{
		if (L.a[i].count < 5)
		{
			cout << L.a[i].word << setw(15) << L.a[i].count << setw(15) << endl;
			for (int j = i; j <= L.length - 1; j++)
			{
				L.a[j] = L.a[j + 1];
			}
			--L.length;
		}
	}
}

//根据count排序,直接插入排序
void Sort(SqList& L)
{
	int i, j;
	for (i = 2; i <= L.length; ++i)
		if (L.a[i].count > L.a[i - 1].count)
		{
			L.a[0] = L.a[i];
			L.a[i] = L.a[i - 1];
			for (j = i - 2; L.a[0].count > L.a[j].count; --j)
				L.a[j + 1] = L.a[j];
			L.a[j + 1] = L.a[0];
		}
}

//输出到文件
void output(SqList& L)
{
	ofstream outfile;
	outfile.open("OutFile.txt");
	outfile << "出现次数大于5的单词:" << endl;
	outfile << "单词" << '\t' << "次数" << endl;
	for (int i = 0; i < L.length; i++)
	{
		outfile << L.a[i].word << '\t' << L.a[i].count << endl;
	}
	outfile.close();
}

//线性表ASL值
int ListASL(SqList& L) {
	int K;
	FILE* fp;
	char ch;//存储单个字符的空间
	danci e;//中间临时变量
	fp = fopen("C://Infile.txt", "r");//打开Infile.txt文件
	while (!feof(fp))//判断是否读取到文件最后
	{
		ch = getc(fp);//获取当前字符
		if (!(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { continue; }
		else {//组成一个单词
			K = 0;//重置在当前单词中字母序号
			e.count = 1;//初始化当前单词频率
			while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch == 39) || ch == '-')//39为'
			{
				if (ch >= 'A' && ch <= 'Z')
					ch += 32;//大写转换成小写
				e.word[K++] = ch;//把当前字母存入当前单词数组,并给K自增
				ch = getc(fp);//读取直到字母,’,-以外的字符
			}
			e.word[K++] = '\0';//结束标志\0,防止经典烫烫烫乱码
		}
		Judge(L, e);//比较是否出现
	}
	int sum = L.length;
	double ASL = 0.0;
	ASL = (double)(sum + 1) / 2.0;
	return ASL;
}

//二叉排序树的存储表示
typedef struct BSTNode {
	danci b;//单词
	struct BSTNode* lchild, * rchild;
} BSTNode, * BSTree;

void InsertBST(BSTree& T, danci e)
{
	if (!T)
	{
		BSTree S = (BSTree)malloc(sizeof(BSTNode));
		S->b = e;//将输入值传入树中
		S->lchild = S->rchild = NULL;
		T = S;
	}
	else if (strcmp(e.word, T->b.word) < 0)//小值左子树
	{
		InsertBST(T->lchild, e);
	}
	else if (strcmp(e.word, T->b.word) > 0)//大值右子树
	{
		InsertBST(T->rchild, e);
	}
	else if (strcmp(e.word, T->b.word) == 0)//等值加频率
	{
		T->b.count++;
	}
}

//从txt中读取数据
void GetFromtree(BSTree& T)
{
	T = NULL;//初始化排序树
	int K;
	FILE* fp;
	char ch;
	danci e;
	fp = fopen("InFile.txt", "r");
	while (!feof(fp))
	{
		ch = getc(fp);//获取当前字符
		if (!(ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { continue; }
		else{
			K = 0;
			e.count = 1;
			while ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == 39 || ch == '-')
			{
				if (ch >= 'A' && ch <= 'Z')
					ch += 32;//转换成小写
				e.word[K++] = ch;//把当前字母存入数组
				ch = getc(fp);
			}
			e.word[K++] = '\0';//结束标志\0
		}
		InsertBST(T, e);
	}
}

//输出显示二叉树(中序遍历)
void MidBST(BSTree& T)
{
	if (T != NULL)
	{
		MidBST(T->lchild);
		cout << T->b.word << setw(15) << T->b.count << setw(15) << endl;
		MidBST(T->rchild);
	}
}

void Insert(BSTree& S, danci e)
{
	if (!S)
	{
		BSTree W = (BSTree)malloc(sizeof(BSTNode));//动态分配内存
		W->b = e;
		W->lchild = W->rchild = NULL;
		S = W;
	}
	else if (e.count >= S->b.count)
	{
		Insert(S->lchild, e);
	}
	else if (e.count < S->b.count)
	{
		Insert(S->rchild, e);
	}
}

//二叉排序树显示频率小于5的单词
void SerachBST(BSTree& T, BSTree& S)
{
	if (T)
	{
		SerachBST(T->lchild, S);
		if (T->b.count >= 5)
		{
			danci e;
			e = T->b;
			Insert(S, e);
		}
		if (T->b.count < 5)
		{
			cout << T->b.word << setw(15) << T->b.count << setw(15) << endl;
		}
		SerachBST(T->rchild, S);
	}
}

//导出到文件
void shuchu(BSTree& S)
{
	if (S != NULL)
	{
		shuchu(S->lchild);
		ofstream outfile;
		outfile.open("OutFile.txt", ios::app);
		outfile << S->b.word << '\t' << S->b.count << endl;
		outfile.close();
		shuchu(S->rchild);
	}
}

int AverASL(BSTree& T, int& n, int& j, int i) {//计算平均查找长度
	if (T) {
		i++; //树高 
		n = n + i;   //结点的树高的和 
		if (AverASL(T->lchild, n, j, i)) {
			j++;   //节点个数 
			if (AverASL(T->rchild, n, j, i)) {
				i--;
				return 1;
			}
		}
	}
	else
		return 1;
}

int TreeASL(BSTree& T) {//单步执行,计算ASL 
	double ASL;
	int n = 0, i = 0, j = 0;
	AverASL(T, n, j, i);
	ASL = (double)n / j;
	return ASL;
}

void menu()
{
	cout << "1、线性表" << endl;
	cout << "2、二叉排序树" << endl;
	cout << "3、退出系统" << endl;
	cout << "请选择你需要的服务,输入数字(1~3)" << endl;
}

void menunext() {
	cout << "1、连续执行至完毕" << endl;
	cout << "2、显示执行时间" << endl;
	cout << "3、单步执行:识别并统计单词" << endl;
	cout << "4、单步执行:删除并显示出现频率低的单词" << endl;
	cout << "5、单步执行:输出其余单词及其频率" << endl;
	cout << "6、单步执行:计算并输出ASL值" << endl;
	cout << "7、返回主菜单" << endl;
}
int main()
{
	while (1)
	{
		menu();
		string cho;
		cout << "请选择需要执行的操作:";
		cin >> cho;
		if (cho != "1" && cho != "2" && cho != "3")
			cout << "您的输入错误,请重新输入!" << endl;
		if (cho == "1")
		{
			while (1)
			{
				SqList L;
				menunext();
				string cho2;
				cout << "请选择需要执行的操作:";
				cin >> cho2;
				if (cho2 == "1")
				{
					InitList(L);
					GetFromlist(L);
					Sort(L);
					Find5(L);
					Show(L);
					output(L);
					cout << "成功输出Outfile.txt文件!" << endl;
				}
				if (cho2 == "2") {
					clock_t start, finish;
					start = clock();
					InitList(L);
					GetFromlist(L);
					Find5(L);
					Sort(L);
					Show(L);
					output(L);
					cout << "成功输出Outfile.txt文件!" << endl;
					finish = clock();
					int time = finish - start;
					cout << "运行的时间为" << time << "毫秒" << endl;
				}
				if (cho2 == "3")
				{
					InitList(L);
					GetFromlist(L);
					cout << "已识别并统计单词!" << endl;
				}
				if (cho2 == "4")
				{
					Find5(L);
				}
				if (cho2 == "5")
				{
					Sort(L);
					Show(L);
					output(L);
					cout << "成功输出Outfile.txt文件!" << endl;
				}
				if (cho2 == "6") {
					InitList(L);
					cout << "线性表ASL值=" << ListASL(L) << endl;
				}
				if (cho2 == "7") { system("cls"); break; }
			}
		}
		if (cho == "2")
		{
			while (1)
			{
				BSTree T, S;
				menunext();
				string cho3;
				cout << "请选择需要执行的操作:";
				cin >> cho3;
				if (cho3 == "1")
				{
					T = NULL;
					S = NULL;
					GetFromtree(T);
					cout << "出现次数小于5的单词如下:" << endl;
					cout << setw(15) << setw(15) << endl;
					SerachBST(T, S);
					cout << "出现次数大于5的单词如下:" << endl;
					cout << setw(15) << setw(15) << endl;
					MidBST(S);
					ofstream outfile;
					outfile.open("OutFile.txt");
					outfile << "出现次数大于5的单词如下:" << endl;
					outfile << "单词" << '\t' << "次数" << endl;
					outfile.close();
					shuchu(S);


				}
				if (cho3 == "2")
				{
					clock_t start, finish;
					start = clock();
					T = NULL;
					S = NULL;
					GetFromtree(T);
					cout << "出现次数小于5的单词如下:" << endl;
					cout << setw(15) << setw(15) << endl;
					SerachBST(T, S);
					cout << "出现次数大于5的单词如下:" << endl;
					cout << setw(15) << setw(15) << endl;
					MidBST(S);
					ofstream outfile;
					outfile.open("OutFile.txt");
					outfile << "出现次数大于5的单词如下:" << endl;
					outfile << "单词" << '\t' << "次数" << endl;
					outfile.close();
					shuchu(S);
					finish = clock();
					int time = finish - start;
					cout << "运行的时间为" << time << "毫秒" << endl;

				}
				if (cho3 == "3")
				{
					T = NULL;
					S = NULL;
					GetFromtree(T);
					cout << "已识别并统计单词!" << endl;
				}
				if (cho3 == "4")
				{
					cout << "出现次数小于5的单词如下:" << endl;
					cout << setw(15) << setw(15) << endl;
					SerachBST(T, S);
				}
				if (cho3 == "5")
				{
					cout << "出现次数大于5的单词如下:" << endl;
					cout << setw(15) << setw(15) << endl;
					MidBST(S);
					ofstream outfile;
					outfile.open("OutFile.txt");
					outfile << "出现次数大于5的单词如下:" << endl;
					outfile << "单词" << '\t' << "次数" << endl;
					outfile.close();
					shuchu(S);
					cout << "成功输出Outfile.txt文件!" << endl;
				}
				if (cho3 == "6") {
					GetFromtree(T);
					cout << "二叉排序树的ASL值=" << TreeASL(T) << endl;
				}
				if (cho3 == "7") { system("cls"); break; }
			}
		}
		if (cho == "3")
			break;
	}
}
  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深海重苹果

谢谢您对我技术的肯定!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值