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

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

数据结构的逻辑设计和物理存储设计

程序采用面向对象的实际方法设计,定义书本结构体:

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;
}

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值