数据结构课程设计
任务一:图书信息管理系统的设计与实现
(一)实验内容:
设计并实现一个图书信息管理系统。根据实验要求设计该系统的菜单和交互逻辑,并编码实现增删改查的各项功能。 该系统至少包含以下功能:
(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;
}
}