目录
一、图书信息管理系统的设计与实现
(一)实验要求
设计并实现一个图书信息管理系统。根据实验要求设计该系统的菜单和交互逻辑,并编码实现增删改查的各项功能。 该系统至少包含以下功能:
- 根据指定图书个数,逐个输入图书信息;
- 逐个显示图书表中所有图书的相关信息;
- 能根据指定的待入库的新图书的位置和信息,将新图书插入到图书表中指定的位置;
- 根据指定的待出库的旧图书的位置,将该图书从图书表中删除;
- 能统计表中图书个数;
- 实现图书信息表的图书去重;
- 实现最爱书籍查询,根据书名进行折半查找,要求使用非递归算法实现,成功返回此书籍的书号和价格;
- 图书信息表按指定条件进行批量修改;
- 利用快速排序按照图书价格降序排序;
- 实现最贵图书的查找;
(二)实验分析
本实验采用顺序表或者链表实现,但由于要在图书序列中实现插入操作,在图书系统中图书数量过大时,采用顺序表效率较低,故本实验采用链表。
管理员对图书的数量,图书号,图书类型,图书名称等进行修改、删除管理,如果普通用户在线借书,图书总数会相应减少。
(三)实验定义
图书信息的定义:
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;
(四)流程图
(五)完整代码
#include <iostream>
#include<cstring>
#pragma warning(disable:4996)
using namespace std;
struct Book {
char no[9];
char name[11];
int price;
};
struct LNode {
Book data;
LNode* next;
};
struct LNodeList {
LNode* first;
int length;
LNodeList() {
first = new LNode;
first->next = nullptr;
length = 0;
}
};
void Input(int n, LNodeList &A) {//根据指定图书个数,逐个输入图书信息
LNode* p = A.first;
while (p->next)p = p->next;
int x = n;
while (n) {
LNode* q = new LNode;
cout << "请输入第" << x - n + 1 << "本图书的八位编号"<<endl;
cin>>q->data.no;//.get(q->data.no,8);
cout << "请输入第" << x - n + 1 << "本图书的书名(十位之内)"<<endl;
cin >> q->data.name;//.get(q->data.name,10);
cout << "请输入第" << x - n + 1 << "本图书的价格"<<endl;
cin>>q->data.price;
n--;
p->next = q;
q -> next = nullptr;
p = q;
}
A.length += x;
}
void Display(LNodeList &A) {//逐个显示图书表中所有图书的相关信息
int n = 1;
if (A.first->next == nullptr) {
cout << "无图书" << endl;
return;
}
LNode* p = A.first;
while (p->next) {
p = p->next;
cout << n << ".图书编码:" << p->data.no << " 书名:" << p->data.name << " 价格:" << p->data.price << endl;
n++;
}
}
void Insert(int n,Book a, LNodeList& A) {//能根据指定的待入库的新图书的位置和信息,将新图书插入到图书表中指定的位置
if (n > A.length + 1||n<1) {
cout << "位置错误" << endl;
return;
}
LNode* p = A.first;
for (;n > 1;n--)p = p->next;
LNode* q = new LNode;
q->data = a;
q->next = p->next;
p->next = q;
A.length++;
}
void Delete(int n, LNodeList& A) {//根据指定的待出库的旧图书的位置,将该图书从图书表中删除
if (n > A.length + 1 || n < 1) {
cout << "位置错误" << endl;
return;
}
LNode* p = A.first;
for (;n > 1;n--)p = p->next;
LNode* q = p->next;
p->next = q->next;
delete q;
A.length--;
}
int Number(LNodeList& A) {//能统计表中图书个数
return A.length;
}
void Clear_repetition(LNodeList& A) {//实现图书信息表的图书去重
LNode* p = A.first;
while (p->next) {
LNode* q_pre = p;
p = p->next;
LNode* q = p;
while (q->next) {
q_pre = q;
q = q->next;
if (strcmp(p->data.no,q->data.no)==0) {
q_pre->next = q->next;
delete q;
q = q_pre;
A.length--;
}
}
}
}
void Search(char title[], LNodeList& A) {//实现最爱书籍查询,根据书名进行折半查找,要求使用非递归算法实现,成功返回此书籍的书号和价格
int n=0;
Book array[10];
LNode* p = A.first;
while (p->next) {
p = p->next;
if (strcmp(p->data.name,title)==0) {
strcpy(array[n].name, p->data.name);
strcpy(array[n].no, p->data.no);
array[n].price = p->data.price;
n++;
}
}
cout << n << endl;
for (int i = 0;i < n;i++) {
cout<<"图书编码:" << array[i].no << " 书名:" << array[i].name << " 价格:" << array[i].price << endl;
}
}
void Sort(LNodeList& A) {
LNode* start = A.first;
Book trans;
while (start->next) {
start = start->next;
LNode* max = start;
LNode* p = start;
while (p->next) {
p = p->next;
if (p->data.price > max->data.price)max = p;
}
trans = max->data;
max->data = start->data;
start->data = trans;
}
}
void Expensive(LNodeList& A) {
if (A.first->next)
{
LNode* p = A.first->next;
LNode* q = p;
while (q->next) {
q = q->next;
if (q->data.price > p->data.price)p = q;
}
cout << "图书编码:" << p->data.no << " 书名:" << p->data.name << " 价格:" << p->data.price << endl;
}
}
int main()
{
LNodeList Test;
int choice = 0;
while (1) {
cout << "******************************************************************************************************************" << endl;
cout << "1.输入图书信息 2.显示图书信息 3.插入图书 4.删除指定图书 5.统计图数个数 6.图书去重 7.最爱书籍查询 8.按价格逆序排序 9.查找最贵图书"<<endl;
cin >> choice;
if (choice < 1 || choice>9) {
cout << "参数错误" << endl;
break;
}
switch (choice) {
case 1:
{
int n1 = 0;
cout << "输入图书的个数" << endl;
cin >> n1;
Input(n1, Test);
}
break;
case 2:
Display(Test);
break;
case 3:
{
int n3 = 0;
Book a;
cout << "插入位置" << endl;
cin >> n3;
cout << "图书编号" << endl;
cin >> a.no;
cout << "图书名" << endl;
cin >> a.name;
cout << "价格" << endl;
cin >> a.price;
Insert(n3, a, Test);
}
break;
case 4:
{
int n4 = 0;
cout << "删除目标位置" << endl;
cin >> n4;
Delete(n4, Test);
}
break;
case 5:
cout<<"图书数量:"<<Number(Test)<<endl;
break;
case 6:
Clear_repetition(Test);
break;
case 7:
{
char title[10];
cout << "最爱书名:" << endl;
cin >> title;
Search(title, Test);
}
break;
case 8:
Sort(Test);
break;
case 9:
Expensive(Test);
break;
}
}
return 0;
}
二、隐式图的搜索问题
(一)实验要求
输入包含三行,每行3各整数,分别为0-8这九个数字,以空格符号隔开,标识问题的初始状态。0表示空格,例如:
2 0 3
1 8 4
7 6 5
(二)实验分析
启发式搜索算法定义了一个估价函数f(n),与问题相关的启发式信息都被计算为一定的 f(n) 的值,引入到搜索过程中。f(n) = g(n) +h(n)其中f(n) 是节点n的估价函数,g(n)是在状态空间中从初始节点到节点n的实际代价,h(n)是从节点n到目标节点最佳路径的估计代价。 在九宫重排问题中,显然g(n)就是从初始状态变换到当前状态所移动的步数,估计函数h(n)估计的是节点n到目标节点的距离,我们可以用欧几里德距离、曼哈顿距离或是两节的状态中数字的错位数来估计。
(三)实验定义
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)步。
(四)流程图
(五)完整代码
#include <vector>
#include <list>
#include <math.h>
#include <iostream>
using namespace std;
const int N = 3; //数组边长大小
struct node { //状态类
int data[N][N]; //九宫数据
int G, H, F; //3个估价函数值
node* parent; //前继指针
node() :G(0), H(0), F(0), parent(NULL) {} //默认构造函数
};
class Astar {
public:
Astar(int startmaze[N][N], int endmaze[N][N]); //初始化Astatr
list<node*> GetPath(); //获取全部路径
void Print(); //显示最佳路径的每一步状态
private:
bool isok(); //判断是否有解
node* findPath(); //获取最佳路径
vector<node*> getSurroundPoints(node* Sudoku) const; //获取0周围的可以移动获得的状态表
bool isInList(list<node*>& list, node point); //判断开启列表中是否包含某点
bool isSame(node* p, node point); //判断两个9宫内数据是否相同
node* getLeastFpoint(); //从开启列表中返回 F 值最小的状态指针
node* finallyload(); //在开启列表中寻找结束状态并返回,否则返回空
node* upSudoku(node*, int, int) const; //返回给定状态0上移的状态指针
node* downSudoku(node*, int, int) const; //返回给定状态0下移的状态指针
node* leftSudoku(node*, int, int) const; //返回给定状态0左移的状态指针
node* rightSudoku(node*, int, int) const; //返回给定状态0右移的状态指针
int GetG(node* Sudoku) { return Sudoku->parent == NULL ? 1 : Sudoku->parent->G + 1; } //从初始状态到指定状态的移动代价(如果是初始节点,则其父节点是空)
int GetH(node* Sudoku); //从指定状态到目标状态的估算成本
int GetF(node* Sudoku) { return GetH(Sudoku) + GetG(Sudoku); } //G,H之和
private:
node startSudoku; //初始九宫
node endSudoku; //目标九宫
list<node*> openList; //开启列表
list<node*> closeList; //关闭列表
list<node*> path; //最佳路径
};
Astar::Astar(int startmaze[N][N], int endmaze[N][N]) { //初始化初始九宫和目标九宫
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
startSudoku.data[i][j] = startmaze[i][j];
endSudoku.data[i][j] = endmaze[i][j];
}
}
}
bool Astar::isok() { //求出逆序对,判断是否有解
int a[9], k = 0;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
a[k++] = startSudoku.data[i][j];
int sum = 0;
for (int i = 0; i < 9; i++)
for (int j = i + 1; j < 9; j++)
if (a[j] && a[i] && a[i] > a[j])
sum++;
return !(sum & 1); //由于目标解为偶数,所以状态的逆序数为偶数才可行,交换空格,逆序数增幅为偶数,故初始节点和目标的节点的逆序数奇偶性相同
}
list<node*> Astar::GetPath() { //根据路径链的头指针获取全部路径列表
node* result = findPath(); //路径链的头指针
while (result) {
path.push_front(result); //插入头部
result = result->parent;
}
// 清空临时开闭列表,防止重复执行 GetPath 导致结果异常
openList.clear();
closeList.clear();
return path;
}
void Astar::Print() { //显示最佳路径的每一步状态
GetPath(); //获取最佳路径
if (isok()) {
cout << "最优路径为:" << endl;
for (auto i : path) {
for (int j = 0; j < N; j++) {
for (int k = 0; k < N; k++) {
cout << i->data[j][k] << " ";
}
cout << endl;
}
cout << endl;
}
}
else cout << "无解!\n";
}
node* Astar::finallyload() { //在开启列表中寻找结束状态并返回,否则返回空
for (auto p : openList)
if (isSame(p, endSudoku))
return p;
return NULL;
}
node* Astar::findPath() { //获取最佳路径
node* p = &startSudoku;
openList.push_back(p); //置入初始状态,内外隔离
while (!openList.empty()) {
auto curPoint = getLeastFpoint(); //找到 F 值最小的点
openList.remove(curPoint); //从开启列表中删除
closeList.push_back(curPoint); //放到关闭列表
//1,找到当前周围4个格中可以移动的格子
auto surroundPoints = getSurroundPoints(curPoint);
for (auto& target : surroundPoints) //2,对某一个状态,如果它不在列表中,加入到开启列表,设置当状态为其父状态,计算 F
if (!isInList(openList, *target) && !isInList(closeList, *target)) {
target->parent = curPoint;
target->G = GetG(target); //从父结点按规则移动的距离
target->H = GetH(target);
target->F = GetF(target);
openList.push_back(target);
}
else {
int tempG = GetG(target);
if (tempG < target->G) { //判断是否有更优秀的到达该结点的路线
target->parent = curPoint;
target->G = tempG;
target->F = GetF(target);
}
}
if (isInList(openList, endSudoku)) //如果目标状态在开启列表中,结束搜索
return finallyload();
}
return NULL;
}
vector<node*> Astar::getSurroundPoints(node* Sudoku) const { //获取0周围的可以移动获得的状态表
vector<node*> test;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (Sudoku->data[i][j] == 0) {
if (i != 0)test.push_back(upSudoku(Sudoku, i, j));
if (i != N - 1)test.push_back(downSudoku(Sudoku, i, j));
if (j != 0)test.push_back(leftSudoku(Sudoku, i, j));
if (j != N - 1)test.push_back(rightSudoku(Sudoku, i, j));
return test;
}
}
}
return test;
}
bool Astar::isSame(node* p, node point) { //判断两个9宫内数据是否相同
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
if (p->data[i][j] != point.data[i][j])
return false;
return true;
}
bool Astar::isInList(list<node*>& list, node point) { //判断开启列表中是否包含某点
for (auto p : list)
if (isSame(p, point))
return true;
return false;
}
node* Astar::getLeastFpoint() { //从开启列表中返回 F 值最小的状态指针
if (!openList.empty()) {
auto resPoint = openList.front();
for (auto& point : openList)
if (point->F < resPoint->F)
resPoint = point;
return resPoint;
}
return NULL;
}
node* Astar::upSudoku(node* Sudoku, int a, int b) const { //返回给定状态0上移的状态指针
node* p = new(node);
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
p->data[i][j] = Sudoku->data[i][j]; //将给定状态复制
p->data[a][b] = p->data[a - 1][b]; //交换0和0上面的数据
p->data[a - 1][b] = 0;
return p;
}
node* Astar::downSudoku(node* Sudoku, int a, int b) const { //返回给定状态0下移的状态指针
node* p = new(node);
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
p->data[i][j] = Sudoku->data[i][j]; //将给定状态复制
p->data[a][b] = p->data[a + 1][b]; //交换0和0下面的数据
p->data[a + 1][b] = 0;
return p;
}
node* Astar::leftSudoku(node* Sudoku, int a, int b) const { //返回给定状态0左移的状态指针
node* p = new(node);
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
p->data[i][j] = Sudoku->data[i][j]; //将给定状态复制
p->data[a][b] = p->data[a][b - 1]; //交换0和0左面的数据
p->data[a][b - 1] = 0;
return p;
}
node* Astar::rightSudoku(node* Sudoku, int a, int b) const { //返回给定状态0左移的状态指针
node* p = new(node);
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
p->data[i][j] = Sudoku->data[i][j]; //将给定状态复制
p->data[a][b] = p->data[a][b + 1]; //交换0和0右面的数据
p->data[a][b + 1] = 0;
return p;
}
int Astar::GetH(node* Sudoku) { //从指定状态到目标状态的估算成本
int h = 0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
if (Sudoku->data[i][j] != 0)
h += abs(Sudoku->data[i][j] / N - i) + abs(Sudoku->data[i][j] % N - j);
}
}
return h; //返回所有数字距离它目标位置的最短距离之和
}
int main() {
int startmaze[N][N] = { {1,2,3},{4,5,6},{7,8,0} }; //初始九宫数据
int endmaze[N][N] = { {1,2,3},{8,0,4},{7,6,5} }; //目标九宫数据
Astar astar(startmaze, endmaze);
astar.Print(); //输出最佳路径
system("pause");
return 0;
}
三、基于线性表和二叉排序树的低频词过滤系统
(一)实验要求
- 对于一篇给定的英文文章,分别利用线性表和二叉排序树来实现单词频率的统计,实现低频词的过滤,并比较两种方法的效率。具体要求如下:
- 读取英文文章文件(Infile.txt),识别其中的单词。
- 分别利用线性表和二叉排序树构建单词的存储结构。当识别出一个单词后,若线性表或者二叉排序树中没有该单词,则在适当的位置上添加该单词;若该单词已经被识别,则增加其出现的频率。
- 统计结束后,删除出现频率低于五次的单词,并显示该单词和其出现频率。
- 其余单词及其出现频率按照从高到低的次序输出到文件中(Outfile.txt),同时输出用两种方法完成该工作所用的时间。
- 计算查找表的ASL值,分析比较两种方法的效率。
- 系统运行后主菜单如下:
当选择1后进入以下界面:
其中选择2时显示利用线性表来实现所有功能所用的时间。
当在主菜单选择2二叉排序树后,进入的界面与上图类同。
(二)实验分析
1. 在统计的过程中,分词时可以利用空格或者标点符号作为划分单词依据,文章中默认只包含英文单词和标点符号。
2.对单词进行排序时,是按照字母序进行的,每个结点还应包含该单词出现的频率。
(三)实验定义
存储结构的定义:
//二叉排序树的存储表示
typedef struct BSTNode{
string WordName; //单词名称
int count; //单词出现频率
struct BSTNode *next;
} BSTNode, *BSTree;
(四)其他伪代码
//线性表伪代码
double LLNode(int M)
{// 统计文章 InFile.txt 中的单词
cout << "**************************************\n" << endl;
FILE* fp;
char ch, array[1000][20]; int i = 0, j = 0, k = 0, jj;
fp = fopen("InFile.txt", "r");// 打开文件
if (fp == NULL) { cout<<"cannot open file\n"<<endl; exit(0); }
LNode* L, * P, * Q;// 定义结构指针
L = Q = new LNode(); L->next = NULL;
while ((ch = fgetc(fp)) != EOF)
{
if (ch == '-' || ch == '\'')continue;//
else if (ch < 65 || (ch > 90 && ch < 97) || ch>122)// 单词
{
if (j == 0)continue;// 输入的是空格或数字或符号
if (L->next) {// 第二个以后的单词进入单链表
P =new LNode(); array[i][j] = 0;
for (jj = 0; array[i][jj] != '\0'; jj++)
P->Wordname[jj] = array[i][jj];
P->Wordname[jj] = '\0'; P->count = 1; P->next = NULL;
while (1)
{
if (strcmp(Q->next->Wordname, P->Wordname) != 0) Q = Q->next;
else { Q->next->count++; break; }// 有相同的单词6
if (!Q->next) { Q->next = P; break; }// 没有相同的单词
}if (M == 1)printf(" %s", array[i]);
}
else {// 第一个单词 ,头结点无数据
P = new LNode(); array[i][j] = 0;
for (jj = 0; array[i][jj] != '\0'; jj++)
P->Wordname[jj] = array[i][jj];
P->Wordname[jj] = '\0'; P->count = 1; P->next = NULL; L->next = P;
if (M == 1)cout << array << endl;;
}
i++; j = 0; Q = L;//Q 指向头结点
}
else if (ch > 64 && ch < 91)array[i][j++] = ch + 32;// 大写字母
else array[i][j++] = ch;
}
if (M == 1)cout << "\n 单词数: %d\n" << i << endl;
if (M == 1 || M == 2 || M == 3)k = wordscount(Q, M);
if (M == 1 || M == 2 || M == 4 || M == 5)L = deletelow(L, M);
if (M == 1 || M == 2 || M == 5)k = tjsyword(L, M);
return (k + 1) / 2.0;
}
//二叉树伪代码
int STBNode(int m)
{
cout << "**************************************\n" << endl;
FILE* fp;
char ch[40], cc[20]; int i = 0, j = 0, l = 1; double kk = 1;
BSTree* B, * SS, * T, * N, * M;
T = B =new BSTree();
fp = fopen("InFile.txt", "r");// 打开文件
if (fp == NULL) { cout<<"cannot open file\n"<<endl; exit(0); }
fscanf(fp, "%s", cc);// 取出根节点
for (i = 0; cc[i] != '\0'; i++)
if (cc[i] > 64 && cc[i] < 91)cc[i] = cc[i] + 32;
strcpy(B->wordname, cc);
B->count = 1; B->lchild = B->rchild = NULL;
if (m == 1)cout << B->wordname << endl;
while (fscanf(fp, "%s", ch) != EOF)
{
j = 0;
for (i = 0; ch[i] != '\0'; i++)
{// 单词处理
if (ch[i] == '-' || ch[i] == '\'')continue;
if (ch[i] > 64 && ch[i] < 91)cc[j++] = ch[i] + 32;
else if (ch[i] > 96 && ch[i] < 123)cc[j++] = ch[i];
else {// 连续多个单词在一起时
cc[j] = '\0'; j = 0;
if (cc[0] != '\0') {
SS = new BSTree();
SS->lchild = SS->rchild = NULL; SS->count = 1;
strcpy(SS->wordname, cc);
if (m == 1)cout << cc << endl; l++;
N = seachbstree(B, cc);// 到树中比较,返回访问树的最后一个节点;
if (N == NULL)continue;
if (!N->lchild || !N->rchild)// 插入到树中
{
if (strcmp(N->wordname, SS->wordname) > 0) { N->lchild = SS; kk++; }
else
if (strcmp(N->wordname, SS->wordname) < 0) { N->rchild = SS; kk++; }
}B = T;
}
}
}cc[j] = '\0';
if (cc[0] != '\0')
{// 读入的是单个单词时
SS = new BSTree();
SS->lchild = SS->rchild = NULL; SS->count = 1;
N = seachbstree(B, cc);
if (m == 1)cout<< cc<<endl; l++;
if (N == NULL)continue;
strcpy(SS->wordname, cc);
if (!N->lchild || !N->rchild)// 插入到树中
{
if (strcmp(N->wordname, SS->wordname) > 0) { N->lchild = SS; kk++; }
else if (strcmp(N->wordname, SS->wordname) < 0) { N->rchild = SS; kk++; }
}
B = T;
}
}
if (m == 1)cout<<"\n 单词数: %d"<<' '<<l<<endl;
M = new BSTree();
if (m == 3) {
asl = LevelOrderTraverse(T) / kk;
cout<<" 单词种数: %d\n"<<' '<<(int)kk<<endl;
}
if (m == 4 || m == 5 || m == 2)
{
deleteLOW(M, T, m);
if (m == 5 || m == 2)
{
FILE* fp = fopen("OutFile1.txt", "w");
clock_t start = clock();
InOrderTraverse(L, m, fp);
clock_t end = clock();
if (m == 5) {
cout << " 高频词个数: %d\n" << (int)gp << endl;;
asl = LevelOrderTraverse(L) / gp; gp = 0;
}
cout<<"\ntime:%.3fs\n"<< double(end - start) / CLOCKS_PER_SEC);
fprintf(fp, "\ntime:%.3fs\n", double(end - start) / CLOCKS_PER_SEC);
fclose(fp);
}
}
}
(五)完整代码
#include<iostream>
#include<fstream>
#include<vector>
#include<time.h>
using namespace std;
constexpr auto NUM = 5; //筛选频率阈值
int isRepeat(vector<vector<char>> WordList, vector<char> word) { //判断单词表WordList中是否存在单词word,若存在则返回位置,否则返回0.
for (int i = 0; i < WordList.size(); i++)
if (WordList[i] == word)return i + 1;
return 0;
}
void BubbleSort(vector<vector<char>>& WordList, vector<int>& WordNum, int n) { //将单词表中的单词按照出现频率从大到小排序(高效的冒泡排序)
int exchange = n - 1, bound;
while (exchange) {
bound = exchange;
exchange = 0;
for (int j = 0; j < bound; j++)
if (WordNum[j] < WordNum[j + 1]) {
swap(WordList[j], WordList[j + 1]);
swap(WordNum[j], WordNum[j + 1]);
exchange = j; //最后一个逆序点
}
}
}
double RecognizeWord(ifstream& infile, vector<vector<char>>& WordList, vector<int>& WordNum) { //存储文件中的单词及数量
double ASL = 0; //平均查找次数
vector<char> word; //单词存储器
if (infile) {
char ch; //字母存储器
int i; //记录单词在单词表中的位置
while ((ch = infile.get()) != EOF) {
if (ch >= 65 && ch <= 90)ch += 32; //大写字母转换成小写
if (ch >= 97 && ch <= 122) //小写字母放在单词后面
word.push_back(ch);
else if (i = isRepeat(WordList, word)) { //判断单词表中是否存在新单词word
WordNum[i - 1]++; //新单词的频率加1
word.clear(); //清空单词存储器
ASL += i; //查找次数增加找到新单词的位置
}
else { //如果没找到
WordNum.push_back(1); //新单词的频率为1
WordList.push_back(word); //在单词表中加入新单词
word.clear(); //清空单词存储器
ASL += WordList.size(); //查找次数增加单词表的长度
}
}
}
else cout << "打开文件错误!" << endl;
cout << "识别完毕!" << endl;
return ASL / WordList.size(); //返回平均查找次数
}
void PrintLittleWord(vector<vector<char>>& WordList, vector<int>& WordNum) { //显示单词表中少于频率阈值的单词输出并从单词表中删除
cout << "单词数少于" << NUM << "次的单词及其数量为:" << endl;
for (int i = 0; i < WordList.size(); i++) {
if (WordNum[i] < NUM) {
for (auto j : WordList[i]) {
cout << j;
}
cout << "(" << WordNum[i] << ")" << endl;
WordNum.erase(begin(WordNum) + i);
WordList.erase(begin(WordList) + i);
i--;
}
}
cout << endl;
}
void PrintResidueWord(vector<vector<char>>& WordList, vector<int>& WordNum) { //显示并在新文件中输入排好序的剩余单词
ofstream ofs; //输出流对象
ofs.open("Outfile.txt", ios::ate); //打开输出文件
if (!ofs) {
cout << "打开文件错误!" << endl;
return;
}
cout << "剩余单词及其数量为:" << endl;
BubbleSort(WordList, WordNum, WordList.size()); //对单词表中的单词进行排序
for (int i = 1; i < WordList.size(); i++) { //最多的是符号位,故从1开始
for (auto j : WordList[i]) { //依次输出单词
cout << j; //在控制台显示数据
ofs << j; //对输出文件进行输出
}
cout << "(" << WordNum[i] << ")" << endl;
ofs << "(" << WordNum[i] << ")" << endl;
}
cout << endl;
ofs.close(); //关闭输出文件
}
int allPath(ifstream& infile, vector<vector<char>>& WordList, vector<int>& WordNum) { //执行所有步骤,返回执行时间(ms)
int begintime, endtime; //开始时间,结束时间
begintime = clock(); //记录开始的时间
double x = RecognizeWord(infile, WordList, WordNum);
PrintLittleWord(WordList, WordNum);
PrintResidueWord(WordList, WordNum);
cout << "ASL值为:" << x << endl;
endtime = clock(); //记录结束的时间
return endtime - begintime; //返回两者差
}
void Linear() { //线性表的人机交互界面
ifstream infile; //输入流对象
infile.open("Infile.txt"); //打开要读取的文件
vector<vector<char>> WordList; //单词表
vector<int> WordNum; //单词频率表
int n, time = 0; //记录用户的输入,记录时间
double ASL; //ASL值
bool t = true; //控制程序退出
bool s1 = false, s3 = false, s4 = false; //控制程序的执行顺序
while (t) {
system("cls"); //防止页面过于繁杂,执行一次清屏
cout << "1.连续执行至完毕\n" << "2.显示执行时间\n" << "3.单步执行,识别并统计单词\n" << "4.单步执行,删除并显示出现频率低单词\n" << "5.单步执行,输出其余单词及其频率\n" << "6.单步执行,计算并输出ASL值\n" << "7.返回主菜单" << endl;
cin >> n;
switch (n) {
case 1:if (s1 || s3) { cout << "单词已经识别,请勿重复识别!" << endl; break; }
else { time = allPath(infile, WordList, WordNum); s1 = true; break; }
case 2:if (s1) { cout << "运行时间为:" << time << "ms" << endl; break; }
else { cout << "程序还未执行!" << endl; break; }
case 3:if (s1 || s3) { cout << "单词已经识别,请勿重复识别!" << endl; break; }
else { ASL = RecognizeWord(infile, WordList, WordNum); s3 = true; break; }
case 4:if (s4) { cout << "单词已删除!" << endl; break; }
else if (s3) { PrintLittleWord(WordList, WordNum); s4 = true; break; }
else { cout << "未识别单词!" << endl; break; }
case 5:if (s4) { PrintResidueWord(WordList, WordNum); break; }
else { cout << "未删除单词!" << endl; break; }
case 6:if (s3) { cout << "ASL值为:" << ASL << endl; break; }
else { cout << "未识别单词!" << endl; break; }
case 7:t = false; break;
default:cout << "选择错误,请重新输入:";
}
system("pause"); //暂停,给用户观察的时间
}
infile.close(); //关闭输入流对象
}
typedef struct BSTNode {
string WordName; //单词名称
int count; //单词出现频率
int height; //单词当前高度
struct BSTNode* lchild, * rchild; //左右子树
}BSTNode, * BSTree;
void InsertnewTree(BSTree& newRoot, BSTree& root) { //递归生成频数二叉排序树
if (newRoot == NULL) {
newRoot = new BSTNode;
newRoot->WordName = root->WordName;
newRoot->count = root->count;
newRoot->height = 0;
newRoot->lchild = NULL;
newRoot->rchild = NULL;
}
else {
if (newRoot->count <= root->count)
return InsertnewTree(newRoot->rchild, root);
else return InsertnewTree(newRoot->lchild, root);
}
}
void PrintLittleWord2(BSTree& newRoot, BSTree& root) { //后序遍历
if (root == NULL) return;
else {
PrintLittleWord2(newRoot, root->lchild);
PrintLittleWord2(newRoot, root->rchild);
if (root->count < NUM) //若频率小于阈值则输出
printf("%s%c%d%s", root->WordName.c_str(), '(', root->count, ")\n");
else InsertnewTree(newRoot, root); //否则插入新树
}
}
int InsertTree(BSTree& root, string word, bool& isInsert, int h = 1) { //递归插入二叉排序树,返回插入所在的高度
if (root == NULL) {
root = new BSTNode;
root->WordName = word;
root->count = 1;
root->height = h;
root->lchild = NULL;
root->rchild = NULL;
isInsert = false;
return h;
}
else {
if (root->WordName.compare(word) == 0) {
root->count++;
isInsert = true;
return h;
}
else if (root->WordName.compare(word) == -1)
return InsertTree(root->lchild, word, isInsert, h + 1);
else return InsertTree(root->rchild, word, isInsert, h + 1);
}
}
double RecognizeWord2(ifstream& infile, BSTree& root) {
double ASL = 0; //平均查找长度
char Word[20]; //存放单词的字符数组
int num = 0; //不同的单词数量
bool isInsert = true; //判断单词表中是否存在该单词(初值不影响使用)
if (infile) {
char ch; //存放字母
int n = 0; //控制位置,记录单词长度
while ((ch = infile.get()) != EOF) {
if (ch >= 65 && ch <= 90)ch += 32; //大写变小写
if (ch >= 97 && ch <= 122) {
Word[n] = ch;
n++;
}
else {
string word(&Word[0], &Word[n]); //字符数组转化成字符串
ASL += InsertTree(root, word, isInsert); //ASL增加插入成功所在的高度数
word.clear(); //清空单词存储器
n = 0; //重置n
if (!isInsert) num++; //如果不存在,单词总数加1
}
}
}
else cout << "打开文件错误!" << endl;
cout << "识别完毕!" << endl;
return ASL / num; //返回平均查找次数
}
void InOrder(ofstream& ofs, BSTree& newRoot) { //反中序输出二叉排序树(从大到小输出)
if (newRoot == NULL)return;
else {
InOrder(ofs, newRoot->rchild);
if (newRoot->count != 0) {
cout << newRoot->WordName.c_str() << '(' << newRoot->count << ')' << endl;
ofs << newRoot->WordName.c_str() << '(' << newRoot->count << ')' << endl;
}
InOrder(ofs, newRoot->lchild);
}
}
void PrintResidueWord2(BSTree& newRoot) { //反中序输出二叉排序树(从大到小输出)
ofstream ofs; //输出流对象
ofs.open("Outfile.txt", ios::ate); //打开输出文件
if (!ofs) {
cout << "打开文件错误!" << endl;
return;
}
cout << "剩余单词及其数量为:" << endl;
BSTree p = newRoot;
while (p->rchild) {
p = p->rchild;
}
p->count = 0; //标记频率最大的符号位
InOrder(ofs, newRoot);
ofs.close(); //关闭输出流对象
}
void PostOrderDelete(BSTree& root) { //后序遍历析构二叉排序树
if (root == NULL)return;
else {
PostOrderDelete(root->rchild);
PostOrderDelete(root->lchild);
delete root;
}
}
int allPath2(ifstream& infile, BSTree newRoot, BSTree root) { //执行所有步骤,返回执行时间(ms)
int begintime, endtime;
begintime = clock();
double x = RecognizeWord2(infile, root);
cout << "单词数少于" << NUM << "次的单词及其数量为:" << endl;
PrintLittleWord2(newRoot, root);
PrintResidueWord2(newRoot);
cout << "ASL值为:" << x << endl;
endtime = clock();
return endtime - begintime;
}
void BinarySortTree() { //排序二叉树的人机交互界面
ifstream infile; //输入流对象
infile.open("Infile.txt"); //打开要读取的文件
BSTree root = NULL; //排序二叉树
BSTree newRoot = NULL; //新排序二叉树
int n, time = 0; //记录用户的输入,记录时间
double ASL;
bool t = true; //控制程序退出
bool s1 = false, s3 = false, s4 = false; //控制程序的执行顺序
while (t) {
system("cls");
cout << "1.连续执行至完毕\n" << "2.显示执行时间\n" << "3.单步执行,识别并统计单词\n" << "4.单步执行,删除并显示出现频率低单词\n" << "5.单步执行,输出其余单词及其频率\n" << "6.单步执行,计算并输出ASL值\n" << "7.返回主菜单" << endl;
cin >> n;
switch (n) {
case 1:if (s1 || s3) { cout << "单词已经识别,请勿重复识别!" << endl; break; }
else { time = allPath2(infile, newRoot, root); s1 = true; break; }
case 2:if (s1) { cout << "运行时间为:" << time << "ms" << endl; break; }
else { cout << "程序还未执行!" << endl; break; }
case 3:if (s1 || s3) { cout << "单词已经识别,请勿重复识别!" << endl; break; }
else { ASL = RecognizeWord2(infile, root); s3 = true; break; }
case 4:if (s4) { cout << "单词已删除!" << endl; break; }
else if (s3) {
cout << "单词数少于" << NUM << "次的单词及其数量为:" << endl;
PrintLittleWord2(newRoot, root); s4 = true; break;
}
else { cout << "未识别单词!" << endl; break; }
case 5:if (s4) { PrintResidueWord2(newRoot); break; }
else { cout << "未删除单词!" << endl; break; }
case 6:if (s3) { cout << "ASL值为:" << ASL << endl; break; }
else { cout << "未识别单词!" << endl; break; }
case 7:t = false; break;
default:cout << "选择错误,请重新输入:";
}
system("pause");
}
PostOrderDelete(root); //释放排序二叉树的内存
PostOrderDelete(newRoot); //释放新排序二叉树的内存
infile.close(); //关闭输入流对象
}
int main() {
int n; //记录用户的输入
bool t = true; //控制程序退出
while (t) {
system("cls");
cout << "1.线性表\n" << "2.二叉排序树\n" << "3.退出系统\n" << "请选择你需要的服务,输入数字(1~3):" << endl;
cin >> n;
switch (n) {
case 1:Linear(); break;
case 2:BinarySortTree(); break;
case 3:t = false; break;
default:cout << "选择错误,请重新输入:";
}
}
return 0;
}