课
程设计一:图书信息管理系统的设计与实现
数据结构的逻辑设计和物理存储设计:
程序采用面向对象的实际方法设计,定义书本结构体:
typedef struct {
char no[14]; //13位书号
char name[41]; //20位书名
double price; //价格
}Book;
存储方法采用线性链表,定义链表结构:
typedef struct LNode{
Book data; //数据域
struct LNode *next; //指针域
}*LinkList;
在主函数中用一个while语句和switch语句实现各种函数的控制调用:
int main() {
LinkList Head=getpch(LNode),Rear = Head; //链表的头结点和尾结点
Head->next = NULL;
int ch; //储存用户的输入
bool t=true; //控制程序的退出
while (t) {
system("cls"); //防止页面过于繁杂,执行一次清屏
cout << "请输入要实现的功能:\n";
cin >> ch;
switch (ch) {
case 1:inputBook(Head, Rear); break; //录入图书
case 2:printBook(Head); break; //显示所有图书的信息
case 3:insetBook(Head, Rear); break; //插入图书
case 4:deleteBook(Head, Rear); break; //删除某一书号的图书
case 5:numBook(Head); break; //统计数量
case 6:deleteRepetitionBook(Head, Rear); break; //图书去重
case 7:cout << favoriteBook(Head) << endl; break; //按书名查找
case 8:changepriceBook(Head); break; //图书信息表价格的修改
case 9:quicksortBook(Head->next, Rear); printBook(Head); break; //根据价格快速排序(降序)
case 10:expensiveBook(Head); break; //最贵图书的查找
case 11:t = false; break; //退出系统
default:cout << "输入失败,请重新输入"; //输入失败
}
system("pause");
}
cout << "程序结束。" << endl;
return 0;
}
程序流程图
算法复杂度分析
由于采用链表的存储结构,在执行录入、插入和删除操作时,时间复杂度是O(1),
在执行查找书号、书名和价格时,时间复杂度是O(n).
算法的空间复杂度是O(n).
代码:
#include<iostream>
#include<cstdio>
using namespace std;
#define getpch(type) (type*)malloc(sizeof(type))
typedef struct {
char no[14]; //13位书号
char name[41]; //20位书名
double price; //价格
}Book;
//链表的定义:
typedef struct LNode{
Book data; //数据域
struct LNode *next; //指针域
}*LinkList;
void inputBook(LinkList Head, LinkList &Rear) { //录入图书
LinkList test;
char ch;
int n = 1;
while (true) {
test = getpch(LNode);
cout << "请输入第" << n << "本书的书号:";
cin >> test->data.no ;
cout << "请输入第" << n << "本书的书名:";
cin >> test->data.name ;
cout << "请输入第" << n << "本书的价格:";
cin>> test->data.price ;
test->next = NULL;
Rear->next = test;
Rear = test; //尾插法
n++;
cout << "是否继续录入?";
cin >> ch;
if (ch != 'y')break;
}
}
void printBook(LinkList Head) { //显示所有图书的信息
LinkList p = Head->next;
cout << "=====图书列表=====" << endl;
while (p != NULL) { //遍历链表,输出数据
printf("%s%c%s%c%.2f%c",p->data.no,' ',p->data.name,' ',p->data.price,'\n');
p = p->next;
}
cout << "0 0 0" << endl;
}
void insetBook(LinkList Head , LinkList Rear) { //插入图书
LinkList test,p=Head;
char ch;
int n,tag=0;
bool t = true;
while (t) {
cout << "请输入插入的位置:";
cin >> n;
if (n < 1) {
cout << "位置错误,请重新插入!";
continue;
}
for (int i = 1; i < n; i++) {
p = p->next;
if (p == NULL) {
cout << "抱歉,入库位置非法!"<<endl;
p = Head;
tag = 1;
i = n;
}
}
if (tag == 1) { //标记位置错误,结束本次循环,开始重新插入
tag = 0;
continue;
}
test = getpch(LNode);
cout << "请输入书号:";
cin >> test->data.no;
cout << "请输入书名:";
cin >> test->data.name;
cout << "请输入价格:";
cin >> test->data.price;
test->next = NULL;
test->next = p->next;
p->next = test;
if (p == Rear)Rear = test;
cout << "是否继续插入?";
cin >> ch;
if (ch != 'y')t = false; //输入y表示继续输入,否则结束输入
}
}
void deleteBook(LinkList Head,LinkList Rear) { //删除某一书号的图书
LinkList p = Head;
char ch;
char num[14];
bool t = true;
int tag = 0;
while (t) {
if (Head->next == NULL) {
cout << "暂无图书,删除失败!" << endl;
return;
}
cout << "请输入待删除图书的书号:";
cin >> num;
while (p->next != NULL) {
if (strcmp(p->next->data.no,num) == 0) {
tag = 1;
if (p->next == Rear)Rear = p;
p->next = p->next->next;
}
else p = p->next;
}
if (tag == 1) {
tag = 0;
printBook(Head);
}
else cout << "出库失败,未找到该图书!"<<endl;
cout << "是否继续删除?";
cin >> ch;
if (ch != 'y')t = false; //输入y表示继续删除,否则结束删除
}
}
int numBook(LinkList Head) { //统计数量
LinkList p = Head->next;
int n = 0;
while (p != NULL) {
n++;
p = p->next;
}
cout << "当前存有" << n << "本书" << endl;
return n;
}
void deleteRepetitionBook(LinkList Head,LinkList Rear) { //图书去重
LinkList p = Head->next,q; //双指针遍历链表
while (p) {
q = p;
while (q->next ) {
if (strcmp(p->data.no, q->next->data.no) == 0) { //判断图书书名是否相同
if (q->next == Rear)Rear = q;
q->next = q->next->next;
}
else q = q->next;
}
p = p->next;
}
numBook(Head);
printBook(Head);
}
int favoriteBook(LinkList Head) { //按书名查找
LinkList p = Head->next;
int n = 0, tag = 0;
char num[21];
cout << "请输入最爱图书的书名:";
cin >> num;
while (p) {
if (strcmp(p->data.name, num) == 0) {
printf("%s%c%s%c%.2f%c", p->data.no, ' ', p->data.name, ' ', p->data.price, '\n');
tag = 1;
n++;
}
p = p->next;
}
if (tag == 0)cout << "抱歉,没有你的最爱!" << endl;
return n;
}
void changepriceBook(LinkList Head) { //图书信息表价格的修改
LinkList p = Head->next;
double allprice = 0; //平均价格
while (p) {
allprice += p->data.price;
p = p->next;
}
allprice /= numBook(Head);
p = Head->next;
while (p) {
if (p->data.price < allprice)p->data.price *= 1.2; //小于平均价格价格上涨20%
else p->data.price *= 1.1; //大于等于平均价格上涨10%
p = p->next;
}
printBook(Head);
}
void quicksortBook(LinkList Head, LinkList Rear) { //递归法根据价格快速排序(降序)
if (Head == NULL || Head == Rear) //如果头指针为空或者链表为空,直接返回
return;
Book t;
LinkList p = Head->next; //用来遍历的指针
LinkList small = Head;
while (p) {
if (p->data.price > small->data.price) { //对于小于轴的元素放在左边
t = small->data; //交换small和p的数据项
small->data = p->data;
p->data= t;
small = small->next;
}
p = p->next;
}
quicksortBook(Head, small);
quicksortBook(small->next, Rear);
}
void expensiveBook(LinkList Head) { //最贵图书的查找
LinkList p = Head->next;
double maxprice = 0; //最贵图书的价格
while (p) {
if (p->data.price > maxprice)maxprice = p->data.price;
p = p->next;
}
p = Head->next;
while (p) {
if (p->data.price == maxprice) //若价格与最贵价格相等则打印信息
printf("%s%c%s%c%.2f%c", p->data.no, ' ', p->data.name, ' ', p->data.price, '\n');
p = p->next;
}
}
int main() {
LinkList Head=getpch(LNode),Rear = Head; //链表的头结点和尾结点
Head->next = NULL;
int ch; //储存用户的输入
bool t=true; //控制程序的退出
while (t) {
system("cls"); //防止页面过于繁杂,执行一次清屏
cout << "请输入要实现的功能:\n";
cin >> ch;
switch (ch) {
case 1:inputBook(Head, Rear); break; //录入图书
case 2:printBook(Head); break; //显示所有图书的信息
case 3:insetBook(Head, Rear); break; //插入图书
case 4:deleteBook(Head, Rear); break; //删除某一书号的图书
case 5:numBook(Head); break; //统计数量
case 6:deleteRepetitionBook(Head, Rear); break; //图书去重
case 7:cout << favoriteBook(Head) << endl; break; //按书名查找
case 8:changepriceBook(Head); break; //图书信息表价格的修改
case 9:quicksortBook(Head->next, Rear); printBook(Head); break; //根据价格快速排序(降序)
case 10:expensiveBook(Head); break; //最贵图书的查找
case 11:t = false; break; //退出系统
default:cout << "输入失败,请重新输入"; //输入失败
}
system("pause");
}
cout << "程序结束。" << endl;
return 0;
}
课程设计二:隐式图的搜索问题
数据结构的逻辑设计和物理存储设计:
程序采用面向对象的实际方法设计,定义九宫状态结构体:
struct node { //状态类
int data[N][N]; //九宫数据
int G,H,F; //3个估价函数值
node *parent; //前继指针
node() :G(0),H(0),F(0), parent(NULL) {} //默认构造函数
};
存储数据采用的是二维数组,与实际结构更加相似,便于算法的设计。
定义A*算法类:
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; //最佳路径
};
在主函数中构造A*算法的对象,并调用其成员函数:
int main() {
int startmaze[N][N] = { {1,2,3},{4,5,6},{7,8,0} }; //初始九宫数据
int endmaze[N][N] = { {0,1,2},{3,4,5},{6,7,8} }; //目标九宫数据
Astar astar(startmaze,endmaze);
astar.Print(); //输出最佳路径
system("pause");
return 0;
}
程序流程图
算法复杂度分析
算法的时间复杂度受GetH函数的影响,选取好的GetH函数可以大幅缩短运行时间。时间复杂度最坏为O(n!)
空间复杂度主要受九宫格内的数据量决定,最好情况下,只需存储最佳路径,故空间复杂度为O(n),最坏情况下,需要存储所有数据的全部可能情况,故空间复杂度为O(n!)。n为格内数据量。
代码:
#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] = { {0,1,2},{3,4,5},{6,7,8} }; //目标九宫数据
Astar astar(startmaze,endmaze);
astar.Print(); //输出最佳路径
system("pause");
return 0;
}
课程设计三:基于线性表和二叉排序树的低频词过滤系统
数据结构的逻辑设计和物理存储设计:
程序采用面向过程的实际方法设计。
线性表存储数据采用的是二维数组:
vector<vector<char>> WordList; //单词表
二叉排序树存储数据采用的是单词结构体,逻辑设计为链式:
typedef struct BSTNode {
string WordName; //单词名称
int count; //单词出现频率
int height; //单词当前高度
struct BSTNode *lchild, *rchild; //左右子树
}BSTNode,*BSTree;
在主函数中调用线性表函数和二叉排序树函数,用while语句和switch语句进行调用控制。
在对二叉排序树进行删除操作时构造了一个新的二叉排序树,将原有符合要求的节点插入到新的二叉排序树中,最后摧毁原来的二叉排序树:
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);
}
}
程序流程图
算法复杂度分析
时间复杂度:对于线性表查找来说,查找一个单词是否在表内的时间复杂度为O(n),对于二叉排序树查找来说,查找一个单词是否在表内的时间复杂度为O()。
空间复杂度:对于线性表和二叉排序树,空间复杂度都是O(n)。
代码:
#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;
}