线性表,二叉排序树低频词过滤系统
一,内容要求:
- 对于一篇给定的英文文章,分别利用线性表和二叉排序树来实现单词频率的统计,实现低频词的过滤,并比较两种方法的效率。具体要求如下:2. 读取英文文章文件(Infile.txt),识别其中的单词。3. 分别利用线性表和二叉排序树构建单词的存储结构。当识别出一个单词后,若线性表或者二叉排序树中没有该单词,则在适当的位置上添加该单词;若该单词已经被识别,则增加其出现的频率。4. 统计结束后,删除出现频率低于五次的单词,并显示该单词和其出现频率。5. 其余单词及其出现频率按照从高到低的次序输出到文件中(Outfile.txt),同时输出用两种方法完成该工作所用的时间。计算查找表的ASL值,分析比较两种方法的效率。
二,部分主要代码:
线性表:
1,读取英文文章文件(Infile.txt),识别其中的单词:
SeqList<Cha> t;
f.open("Infile.txt", ios::in);
if (!f) {
cout << "不存在文件Infile.txt" << endl;
}
else {
cout << "存在文件Infile.txt" << endl;
while (f.peek()!= EOF) {
f >>tem.name;
t.Insert(1, tem);
}
}
f.close();
2,利用线性表和二叉排序树构建单词的存储结构。当识别出一个单词后,若线性表或者二叉排序树中没有该单词,则在适当的位置上添加该单词;若该单词已经被识别,则增加其出现的频率。
SeqList<Cha> s;//定义顺序表对象;
SeqList<Cha> t;//中转
for (int i = 0; i < t.GetLength(); ++i) {
tem = t.Get(i + 1);
s.Insert1(1, tem);//把tem插入位置一
}
PrintList(s);
3,删除出现频率低于五次的单词,并显示该单词和其出现频率。
for (int i = 0; i < s.GetLength(); ++i) {//先遍历
cout << s.GetLength() << endl;
tem = s.Get(i + 1);
if (tem.fq < 5) {
cout << "被删:" << endl;
PrintSd(s.Get(i + 1));
s.Delete(i + 1);
i--;//!!!!!!删了这位置数据,后面所有数据会前移动,如果让i++,会跳过新移到这位置的数据;
}
}
4,其余单词及其出现频率按照从高到低的次序输出到文件中(Outfile.txt),同时输出用两种方法完成该工作所用的时间。
clock_t start, finish;
start = clock();
s.QuickSort(0, s.GetLength() - 1);
ofstream g("Outfile.txt");
if (!g) {
cout << "不能打开文件Outfile.txt" << endl;
exit(1);
}
int i = 1;
int len = s.GetLength();
if (len >= 2) {
while (i <= len - 1) {
tem = s.Get(i);
g << tem.name << " ";//单词之间的间隔;
++i;
}
tem = s.Get(i);
g << tem.name;
}
else {
if (len == 1) {
tem = s.Get(1);
g << tem.name; }
else {
cout << "无记录" << endl;
}
}
g.close();
cout << "保存完毕" << endl;
finish = clock();
cout << finish - start << "/" << CLOCKS_PER_SEC << " (s) " << endl;
5,计算查找表的ASL值。
template < class T >
void SeqList<T>::Asl() {
double tem;
tem = (length + 1) / 2;
cout << "若查找成功,ASL=" <<tem<< endl;
tem = length;
cout << "若查找不成功,ASL=" << tem << endl;
}
二叉排序树:
1,读取英文文章文件(Infile.txt),识别其中的单词:
SeqList<Cha> t;
f.open("Infile.txt", ios::in);
if (!f) {
cout << "不存在文件Infile.txt" << endl;
}
else {
cout << "存在文件Infile.txt" << endl;
while (f.peek()!= EOF) {
f >>tem.name;
t.Insert(1, tem);
}
}
f.close();
2,利用线性表和二叉排序树构建单词的存储结构。当识别出一个单词后,若线性表或者二叉排序树中没有该单词,则在适当的位置上添加该单词;若该单词已经被识别,则增加其出现的频率。
BSTree<Cha> bst1(t);//中转
bst = bst1;
bst.Cout();
template < class T >
BSTNode* BSTree<T>::Insert(BSTNode* root, BSTNode* s) {//二叉排序树递归插入函数
if (root ==NULL) return s;//递归停止条件
else {
if (s->cha.fq < root->cha.fq) {//寻找合适的位置
root->lchild = Insert(root->lchild, s); }
else {
root->rchild = Insert(root->rchild, s);
}
}
return root;
}template < class T >
BSTree<T>::BSTree(SeqList<T> sq) {//二叉排序树带参构造函数
root = NULL;
for (int i = 0; i < sq.GetLength(); ++i) {//遍历线性表
BSTNode* s = new BSTNode;//s为插入的结点
s->cha = sq.Get(i + 1);
s->lchild = NULL;
s->rchild = NULL;
root = Insert(root, s);
}
}
3,统计结束后,删除出现频率低于五次的单词,并显示该单词和其出现频率。
template < class T >
void BSTree<T>::Release(BSTNode* root) {
if (root == NULL) {
return;
}
Release(root->lchild);
Release(root->rchild);
delete root;
}
template < class T >void BSTree<T>::Cout(){
cout << "单词名 频率" << endl;
cout << "----------------------------" << endl;
Inorder(Getroot());
cout << "----------------------------" << endl;}
template < class T >
void BSTree<T>::Inorder(BSTNode* root) {
if (root == NULL) {
return;
}
Inorder(root->lchild);
cout << root->cha.name << setw(9) << root->cha.fq << endl;
Inorder(root->rchild);
}
4,其余单词及其出现频率按照从高到低的次序输出到文件中(Outfile.txt),同时输出用两种方法完成该工作所用的时间。
clock_t start, finish;
start = clock();
bst.outfile2(bst.Getroot());
cout << "保存完毕" << endl;
finish = clock();
cout << finish - start << "/" << CLOCKS_PER_SEC << " (s) " << endl;
5,计算查找表的ASL值
template < class T >
void BSTree<T>::Asl() {
BSTNode* t = this->Getroot();
if (t == NULL)return;
queue<BSTNode*> a;//暂时存需拓展的树根结点
queue<BSTNode*> parent;//暂时存已经拓展的结点
BSTNode* p;
int n = 0; //所有元素个数
int m = 0; //所有补上的叶结点数
int i = 1;//树的层数
int w[10] = { 0 };//每层元素个数
int r[10] = { 0 };//每层补上的叶结点数
int fu[10] = { 0 };//每层父节点(已经拓展的结点)数量,辅助清parent队列
int flag = 1;
a.push(t);
while (!a.empty())
{
p = a.front();
a.pop();//删第一个
if (p->rchild == NULL || p->lchild == NULL) {
r[i]++;//每层补上的叶结点数
m++;
}
w[i]++; //每层元素个数
n++; //结点个数
if (a.empty())
{
if (flag == 1) {//只用一次的换行,给第一层用
i++;
flag++;
}
//if (p->lchild == NULL && p->rchild == NULL)break;//跳出while循环;
}
if (p->lchild != NULL || p->rchild != NULL)
{
if (p->lchild != NULL)a.push(p->lchild);
if (p->rchild != NULL)a.push(p->rchild);
parent.push(p);
if (fu[1] == 0)
fu[1] = 1;//根节点
else
fu[i]++; //父节点数量
}
//一层结束,父节点队列出队,清parent
//左节点不为空,右节点为空, 且p等于左节点
if ((parent.front()->lchild != NULL) && p == parent.front()->lchild)
{
for (int j = 0; j < fu[i]; j++)
parent.pop();
i++;//一层结束
}
//右节点不为空,左节点为空, 且p等于右节点
else if ((parent.front()->rchild != NULL) && p == parent.front()->rchild)
{
for (int j = 0; j < fu[i]; j++)
parent.pop();
i++;//一层结束
}
}
int s = 0, z = 0;
for (int j = 0; j <= i; j++)//共i+1层
{
s += j * w[j];//所有(层元素个数*层数)的总数
z += j * r[j];// 所有(层补上的叶结点数* 层数)的总数
}
cout << "查找成功时的平均检索长度(ASL)=" << endl;
cout << s << '/' << n << endl;
cout << "查找失败时的平均检索长度(ASL)=" << endl;
cout << z << '/' << m << endl;
}
三,效果截图
Infile.txt:
排好的outfile.txt
二叉排序树同理
四,总结:
完整代码由一个单词结构体Cha,一个顺序表,一个二叉树结点结构体BSTNode,一个二叉排序树类BSTree组成和主函数。
主函数中注意switch的case的break不要漏,不然无法返回上一级菜单;顺序表的删除注意不要漏掉遍历的元素,因为每删除一个,后面的元素都会变一次位置。用文件流读入数据时注意最后不要留空行,不然最后一个元素会被读取两次,解决:重新搞一个txt文件读取。
完整代码:
#include<iostream>
#include<string>
#include<fstream>
#include<iomanip>
#include<ctime>
#include<queue>
#include<vector>
#define MaxSize 100
using namespace std;
typedef struct {
string name; //单词名
double fq; //对应频率
}Cha;
vector<Cha> v(0);//存放要输出到文件的数据;
typedef struct BiNode {
Cha cha;
struct BiNode* lchild, * rchild;
} BSTNode;
template < class T >
class SeqList
{
public:
SeqList() { length = 0; } //无参构造函数
SeqList(T a[], int n); //有参构造函数
~SeqList(); //析构函数为空
int Search(string x);//名字查找
void Insert(int i, T x); //在顺序表中第i个位置插入值为x的元素(i为逻辑号)
void Insert1(int i, T x);
void Delete(int i);//删除顺序表的第i个元素(i为逻辑号)
void QuickSort(int low, int high);//降序排列
void Asl();//ASL
int GetLength() { return length; } //求顺序表的长度
T Get(int i); //取顺序表的第i个元素(i为逻辑号)
private:
T data[MaxSize]; //存放数据的数组
int length; //顺序表的长度
};
template < class T >
SeqList<T>::SeqList(T a[], int n) { //有参构造函数
if (n > MaxSize) cout << "参数错误" << endl;
for (int i = 0; i < n; ++i) {
data[i] = a[i];
}
length = n;//初始化
}
template < class T >
int SeqList<T>::Search(string x) {
for (int i = 0; i < length; ++i) {
if (data[i].name == x) {
data[i].fq++;
return 1;
}
}
return 0;
}
template < class T >
void SeqList<T>::Insert(int i, T x) {//i为逻辑号
cout << x.name << endl;
if (Search(x.name)) {
return;
}
else {
if (length >= MaxSize) cout << "上溢" << endl;
if (i > length + 1) cout << "位置错误" << endl;
for (int j = length; j >= i; --j) { //遍历调整元素位置;
data[j] = data[j - 1];
}
data[i - 1].fq = 1;
data[i - 1].name = x.name;//同时改变length
++length;
}
}
template < class T >
void SeqList<T>::Insert1(int i, T x) {
if (length >= MaxSize) cout << "上溢" << endl;
if (i<1 || i>length + 1) cout << "位置错误" << endl;
for (int j = length; j >= i; --j) { //遍历调整元素位置;
data[j] = data[j - 1];
}
data[i - 1] = x;//同时改变length
++length;
}
template < class T >
void SeqList<T>::Delete(int i) {
if (length == 0) cout << "下溢" << endl;
if (i<1 || i>length) cout << "位置错误" << endl;
T x = data[i - 1];
for (int j = i; j < length; ++j) {
data[j - 1] = data[j]; //删除的元素后的元素全部前移动一位,以达到删除的目的;
}
--length;//同时改变length
return;
}
template < class T >
void PrintList(SeqList<T> d) { //全部输出;
cout << " 单词数: " << d.GetLength() << endl;
if (d.GetLength() > 0) {//有则输出
cout << "单词名 频率" << endl;
cout << "----------------------------" << endl;
for (int i = 1; i <= d.GetLength(); ++i) {
Cha s = d.Get(i);
cout << s.name << setw(9) << s.fq << endl;
}
cout << "----------------------------" << endl;
}
else {//无
cout << "无记录" << endl;
}
}
template < class T >
void PrintSd(T s) { //输出指定位置元素;
cout << "单词名 频率" << endl;
cout << "----------------------------" << endl;
cout << s.name << setw(9) << s.fq << endl;
cout << "----------------------------" << endl;
}
template < class T >
void SeqList<T>::QuickSort(int low, int high) {
if (low < high) //递归终止条件
{
int i = low, j = high;
T x = data[low];
while (i < j) {
while (i < j && data[j].fq <= x.fq) j--; //先从右边开始
if (i < j) data[i++] = data[j];
while (i < j && data[i].fq >= x.fq) i++;
if (i < j) data[j--] = data[i];
}
data[i] = x; //i已经排列好
QuickSort(low, i - 1);//递归
QuickSort(i + 1, high);
}
}
template < class T >
void SeqList<T>::Asl() {
double tem;
tem = (length + 1) / 2;
cout << "若查找成功,ASL=" << tem << endl;
tem = length;
cout << "若查找不成功,ASL=" << tem << endl;
}
template < class T >
T SeqList<T>::Get(int i) { //取顺序表的第i个元素
if (i > 0 && i <= length) //保证i不会引起数组overflow;
return data[i - 1];
}
template < class T >
SeqList<T>::~SeqList() {
}
template < class T >
class BSTree {
public:
BSTree() { root = NULL; };
BSTree(SeqList<T> sq);//二叉排序树带参构造函数
~BSTree() {}
void Release(BSTNode* root);//释放root为根的树
BSTNode* Getroot() { return root; }
BSTNode* Insert(BSTNode* root, BSTNode* s);//二叉排序树递归插入函数
void Inorder(BSTNode* root);//中序遍历
void Cout();//输出
void Asl();//求Asl()
void outfile2(BSTNode* root);//输出到文件并输出执行时间
private:
BSTNode* root;
};
template < class T >
BSTNode* BSTree<T>::Insert(BSTNode* root, BSTNode* s) {//二叉排序树递归插入函数
s->lchild = NULL;
s->rchild = NULL;
if (root == NULL) return s;//递归停止条件
else {
if (s->cha.fq < root->cha.fq) {//寻找合适的位置
root->lchild = Insert(root->lchild, s);
}
else {
root->rchild = Insert(root->rchild, s);
}
}
return root;
}
template < class T >
BSTree<T>::BSTree(SeqList<T> sq) {//二叉排序树带参构造函数
root = NULL;
for (int i = 0; i < sq.GetLength(); ++i) {//遍历线性表
BSTNode* s = new BSTNode;//s为插入的结点
s->cha = sq.Get(i + 1);
s->lchild = NULL;
s->rchild = NULL;
root = Insert(root, s);
}
}
template < class T >
void BSTree<T>::Release(BSTNode* root) {//释放root为根的树
if (root == NULL) {
return;
}
Release(root->lchild);
Release(root->rchild);
delete root;
root == NULL;
}
template < class T >
void BSTree<T>::Inorder(BSTNode* root) {//中序遍历
if (root == NULL) {
return;
}
Inorder(root->lchild);
v.push_back(root->cha);
cout << root->cha.name << setw(9) << root->cha.fq << endl;
Inorder(root->rchild);
}
template < class T >
void BSTree<T>::outfile2(BSTNode* root) {//输出到文件并输出执行时间
if (root == NULL)return;
ofstream g("Outfile.txt");
if (!g) {
cout << "不能打开文件Outfile.txt" << endl;
exit(1);
}
v.clear();
this->Inorder(root);
reverse(v.begin(), v.end());
for (int i = 0; i < v.size(); ++i) {
g << v[i].name << " ";//单词之间的间隔;
}
g.close();
cout << "保存完毕" << endl;
}
template < class T >
void BSTree<T>::Cout() {
cout << "单词名 频率" << endl;
cout << "----------------------------" << endl;
Inorder(Getroot());
cout << "----------------------------" << endl;
}
template < class T >
void BSTree<T>::Asl() {
BSTNode* t = this->Getroot();
if (t == NULL)return;
queue<BSTNode*> a;//暂时存需拓展的树根结点
queue<BSTNode*> parent;//暂时存已经拓展的结点
BSTNode* p;
int n = 0; //所有元素个数
int m = 0; //所有补上的叶结点数
int i = 1;//树的层数
int w[10] = { 0 };//每层元素个数
int r[10] = { 0 };//每层补上的叶结点数
int fu[10] = { 0 };//每层父节点(已经拓展的结点)数量,辅助清parent队列
int flag = 1;
a.push(t);
while (!a.empty())
{
p = a.front();
a.pop();//删第一个
if (p->rchild == NULL || p->lchild == NULL) {
r[i]++;//每层补上的叶结点数
m++;
}
w[i]++; //每层元素个数
n++; //结点个数
if (a.empty())
{
if (flag == 1) {//只用一次的换行,给第一层用
i++;
flag++;
}
//if (p->lchild == NULL && p->rchild == NULL)break;//跳出while循环;
}
if (p->lchild != NULL || p->rchild != NULL)
{
if (p->lchild != NULL)a.push(p->lchild);
if (p->rchild != NULL)a.push(p->rchild);
parent.push(p);
if (fu[1] == 0)
fu[1] = 1;//根节点
else
fu[i]++; //父节点数量
}
//一层结束,父节点队列出队,清parent
//左节点不为空,右节点为空, 且p等于左节点
if ((parent.front()->lchild != NULL) && p == parent.front()->lchild)
{
for (int j = 0; j < fu[i]; j++)
parent.pop();
i++;//一层结束
}
//右节点不为空,左节点为空, 且p等于右节点
else if ((parent.front()->rchild != NULL) && p == parent.front()->rchild)
{
for (int j = 0; j < fu[i]; j++)
parent.pop();
i++;//一层结束
}
}
int s = 0, z = 0;
for (int j = 0; j <= i; j++)//共i+1层
{
s += j * w[j];//所有(层元素个数*层数)的总数
z += j * r[j];// 所有(层补上的叶结点数* 层数)的总数
}
cout << "查找成功时的平均检索长度(ASL)=" << endl;
cout << s << '/' << n << endl;
cout << "查找失败时的平均检索长度(ASL)=" << endl;
cout << z << '/' << m << endl;
}
int main() {
ifstream f;//调用外部文件用;
int w;//键盘输入的数据位置;
int op, sw; //switch选case的凭依;
Cha tem; // 辅助SeqList<Sd> s的函数Insert()使用;
SeqList<Cha> s;//定义顺序表对象;
SeqList<Cha> t;//中转
BSTree<Cha> bst;
string na;//辅助输入名字;
int whil = 1;
int whi = 1; //菜单进入和跳出的条件;
int wh = 1;//菜单case1循环输入;
f.open("Infile.txt", ios::in);
if (!f) {
cout << "不存在文件Infile.txt" << endl;
}
else {
cout << "存在文件Infile.txt" << endl;
while (f.peek() != EOF) {
f >> tem.name;
t.Insert(1, tem);
}
}
f.close();
while (whil) {
whi = 1;//重置;
cout << " 主菜单 " << endl;
cout << " 1.顺序表" << endl;
cout << " 2.二叉排列树" << endl;
cout << " 3.退出" << endl;
cout << " 输入(1~3) " << endl;
cin >> sw;
switch (sw) {
case 1:
while (whi) {
cout << " 主菜单 " << endl;
cout << " 1.识别并统计单词" << endl;
cout << " 2.删除显示低频单词" << endl;
cout << " 3.输出所有单词" << endl;
cout << " 4.计算输出ASL" << endl;
cout << " 5.输出输出到文件的执行时间" << endl;
cout << " 6.返回" << endl;
cout << " 输入(1~6) " << endl;
cin >> op;
switch (op) {
case 1:
for (int i = 0; i < t.GetLength(); ++i) {
tem = t.Get(i + 1);
s.Insert1(1, tem);//把tem插入位置一
}
PrintList(s);
break;
case 2:
for (int i = 0; i < s.GetLength(); ++i) {//先遍历
cout << s.GetLength() << endl;
tem = s.Get(i + 1);
if (tem.fq < 5) {
cout << "被删:" << endl;
PrintSd(s.Get(i + 1));
s.Delete(i + 1);
i--;//!!!!!!删了这位置数据,后面所有数据会前移动,如果让i++,会跳过新移到这位置的数据;
}
}
break;
case 3:
PrintList(s);
break;
case 4:
s.Asl();
break;
case 5: {
clock_t start, finish;
start = clock();
s.QuickSort(0, s.GetLength() - 1);
ofstream g("Outfile.txt");
if (!g) {
cout << "不能打开文件Outfile.txt" << endl;
exit(1);
}
int i = 1;
int len = s.GetLength();
if (len >= 2) {
while (i <= len - 1) {
tem = s.Get(i);
g << tem.name << " ";//单词之间的间隔;
++i;
}
tem = s.Get(i);
g << tem.name;
}
else {
if (len == 1) {
tem = s.Get(1);
g << tem.name;
}
else {
cout << "无记录" << endl;
}
}
g.close();
cout << "保存完毕" << endl;
finish = clock();
cout << finish - start << "/" << CLOCKS_PER_SEC << " (s) " << endl;
break;
}
case 6:
whi = 0;
break;
default:continue;
}
}
break;
case 2:
while (whi) {
cout << " 主菜单 " << endl;
cout << " 1.识别并统计单词" << endl;
cout << " 2.删除显示低频单词" << endl;
cout << " 3.输出所有单词" << endl;
cout << " 4.计算输出ASL" << endl;
cout << " 5.输出输出到文件的执行时间" << endl;
cout << " 6.返回" << endl;
cout << " 输入(1~6) " << endl;
cin >> op;
switch (op) {
case 1: {
BSTree<Cha> bst1(t);//中转
bst = bst1;
bst.Cout();
break;
}
case 2: {
s = t;
for (int i = 0; i < s.GetLength(); ++i) {//先遍历
cout << s.GetLength() << endl;
tem = s.Get(i + 1);
if (tem.fq < 5) {
cout << "被删:" << endl;
PrintSd(s.Get(i + 1));
s.Delete(i + 1);
i--;//!!!!!!删了这位置数据,后面所有数据会前移动,如果让i++,会跳过新移到这位置的数据;
}
}
BSTree<Cha> bst1(s);
bst = bst1;
break;
}
case 3:
bst.Cout();
break;
case 4:
bst.Asl();
break;
case 5: {
clock_t start, finish;
start = clock();
bst.outfile2(bst.Getroot());
finish = clock();
cout << finish - start << "/" << CLOCKS_PER_SEC << " (s) " << endl;
break;
}
case 6:
whi = 0;
break;
default:continue;
}
}
break;//!!!!!!不可无,不然无法返回上一级目录;
case 3:
whil = 0;
break;
default: continue;
}
}
bst.Release(bst.Getroot());//人为析构,自己析构的话case 1构建了,然后又析构了,别的case用不了;
return 0;
}