《数据结构课程设计》预习日志

目录

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

(一)实验要求

(二)实验分析

  (三)实验定义

         (四)流程图

二、隐式图的搜索问题

(一)实验要求

(二)实验分析

         (三)实验定义

         (四)流程图

三、基于线性表和二叉排序树的低频词过滤系统

         (一)实验要求

         (二)实验分析

         (三)实验定义

         (四)其他伪代码


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

(一)实验要求

设计并实现一个图书信息管理系统。根据实验要求设计该系统的菜单和交互逻辑,并编码实现增删改查的各项功能。 该系统至少包含以下功能:

  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;

(四)流程图

(五)完整代码

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

三、基于线性表和二叉排序树的低频词过滤系统

(一)实验要求

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

当选择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;
}

树状显示二叉树: 编写函数displaytree(二叉树的根指针,数据值宽度,屏幕的宽度)输出树的直观示意图。输出的二叉树是垂直打印的,同层的节点在同一行上。 [问题描述] 假设数据宽度datawidth=2,而屏幕宽度screenwidth为64=26,假设节点的输出位置用 (层号,须打印的空格数)来界定。 第0层:根在(0,32)处输出; 第1层:因为根节点缩进了32个空格,所以下一层的偏移量(offset)为32/2=16=screenwidth/22。即第一层的两个节点的位置为(1,32-offset),(1,32+offset)即(1,16),(1,48)。 第二层:第二层的偏移量offset为screenwidth/23。第二层的四个节点的位置 分别是(2,16-offset),(2,16+offset),(2,48-offset),(2,48+offset)即(2,8),(2,24),(2,40),(2,56)。 …… 第i层:第i层的偏移量offset为screenwidth/2i+1。第i层的每个节点的位置是访问第i-1层其双亲节点时确定的。假设其双亲的位置为(i-1,parentpos)。若其第i层的节点是其左孩子,那末左孩子的位置是(i,parentpos-offset),右孩子的位置是(i,parentpos+offset)。 [实现提示] 利用二叉树的层次遍历算法实现。利用两个队列Q,QI。队列Q中存放节点信息,队列QI中存相应于队列Q中的节点的位置信息,包括层号和需要打印节点值时需要打印的空格数。当节点被加入到Q时,相应的打印信息被存到QI中。二叉树本身采用二叉链表存储。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值