文件说明
文件名 | 数据生成.cpp | test.cpp | BST.h | BST.cpp |
---|---|---|---|---|
文件内容 | 用于生成测试数据的程序 | 折半查找和测试主程序 | BST类声明 | BST类实现 |
1.设计 BST 的左右链存储结构,并实现 BST 插入(建立)、删除、查找和排 序算法。
设计的BST类
#pragma once
#include<fstream>
using namespace std;
#include<string>
#define max 10000
typedef struct celltype {
int data;
celltype* lchild, * rchild;
}BSTNode;//存储结构
typedef BSTNode* BSTREE;
class BST {
public:
BST(string filename);//输入文件名构建查找树
int bst_insert(BSTREE &T,int k);//插入函数
BSTREE bst_search(int k, int& len);//查找
int delete_(BSTREE &BT);//辅助删除函数,用于处理继承节点
void bst_delete(BSTREE& BT,int k);//删除函数
void visit(BSTREE T);//访问节点函数
void bst_sort(BSTREE BT);//排序,实质上就是中序遍历
void search_cost();//计算平均查找长度查找,只适用于特定数据序列类型
public:
BSTREE T;//二叉查找树
double len_s;//平均查找成功次数
double len_f;//平均查找失败长度
int f_data[max];//用于保存文件输入流的数据
int total;//总结点数
};
插入和建立:
BST::BST(string filename)//输入文件名构建查找树
{
ifstream ifs(filename, ios::in);//以读方式打开图文件
if (!ifs.is_open())
{
cout << "文件打开失败" << endl;
return;
}
int num;//输入流承载体
int i = 0;
total = 0;
while (ifs >> num) {//读取文件进数组
f_data[i++] = num;
}
for (int j = 0; j < i; j++)
bst_insert(T, f_data[j]);//逐个插入
}
int BST::bst_insert(BSTREE &BT,int k)//插入函数
{
if (BT == NULL) {//根节点为空
BT = new BSTNode;
BT->data = k;
BT->lchild = NULL;
BT->rchild = NULL;
total++;
return 1;//创建成功,返回1
}
else if (k == BT->data)
return 0;//存在相同关键字,插入失败
else if (k < BT->data)//左小
return bst_insert(BT->lchild, k);
else//右大
return bst_insert(BT->rchild, k);
}
查找
BSTREE BST::bst_search(int k, int& len)//查找
{
BSTREE p = T;
while (p) {
len++;
if (k == p->data) {
cout<< "找到元素:" << p->data <<endl;
return p;//找到直接返回
}
if (k > p->data)
p = p->rchild;
else
p = p->lchild;
}
if (p == NULL)
cout << "没找到该元素" <<k<< endl;
return p;//查找失败返回空
}
删除
int BST::delete_(BSTREE &BT)//辅助删除函数,用于处理继承节点
{//目的是找到中序遍历,ABC,用A中最后一个元素或C中第一个元素替换掉B
int tmp;//这里是找到右边的最小节点
if (BT->lchild == NULL) {//没有左子树,是最小的节点
tmp = BT->data;
BT = BT->rchild;//替换
return tmp;
}//T就是最小
else return delete_(BT->lchild);//有左子树,一直往左下找
}//因为是多重递归,要用return才能返回值到最开始调用函数中
void BST::bst_delete(BSTREE& BT,int k)//删除函数
{
if (BT != NULL) {
if (k < BT->data)
bst_delete(BT->lchild, k);//一直往下查找
else if (k > BT->data)//不能直接用search,因为返回的是局部变量,如果返回引用的话,又会导致头节点变化
bst_delete(BT->rchild, k);
else//找到待删除节点
if (BT->rchild == NULL) {//右子树为空
cout << "成功删除元素:" << BT->data << endl;
BT = BT->lchild;
total--;
}
else if (BT->lchild == NULL) {//左子树为空
cout << "成功删除元素:" << BT->data << endl;
BT = BT->rchild;
total--;
}
else {
cout << "成功删除元素:" << BT->data << endl;
BT->data = delete_(BT->rchild);
total--;
}
}
}
排序
void BST::visit(BSTREE BT)//访问节点函数
{
cout << BT->data << " ";
}
void BST::bst_sort(BSTREE BT)//排序,实质上就是中序遍历
{
int i = 0;
if (BT != NULL) {
bst_sort(BT->lchild);
visit(BT);
bst_sort(BT->rchild);
}
}
统计查找长度
void BST::search_cost()//只适用于特定数据序列类型
{
int s = 0;//查找成功节点个数
int sl = 0;//查找成功总长度
int f = 0;//查找失败节点个数
int fl = 0;//查找失败总长度
for (int i = 0; i <= 2048; ++i) {
if (i % 2) {//奇数
s++;
bst_search(i, sl);
}
else {
f++;
bst_search(i, fl);
}
}
len_s = (double)sl / s;
len_f = (double)fl / f;
cout << "查找成功的平均查找长度为:" << len_s << endl;
cout << "查找失败的平均查找长度为:" << len_f << endl;
}
建立、插入、查找、删除、排序:
使用数据:(生成源程序下面有)
测试代码:
int main(void) {
//测试BST——————————————————
BST T1("input1.txt");
cout<<"初始总节点数为" << T1.total << endl;
int len = 0;
T1.bst_search(2047, len);//查2047
T1.bst_delete(T1.T,5);//先删5
cout << "删除5后总节点数为" << T1.total << endl;
int i = 0;
T1.bst_search(5,i);//再找5
T1.bst_insert(T1.T, 4);//再插入4
cout << T1.total << endl;
cout << "插入4后总节点数为" << T1.total << endl;
T1.bst_sort(T1.T);
//T1.search_cost();
}
测试结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-23axrooo-1677332245602)(2022120067-刘虹志-作业5.assets/1.png)]
2.实现折半查找算法。
代码:
int Bin_Search(int a[], int n, int key, int& len) {//折半查找
int low = 0, high = n, mid;//这里直接传入BST类中的f_data数组就行了
while (low <= high) {
mid = (low + high) / 2;
len++;
if (a[mid] == key)
return mid;//返回数组下标
else if (a[mid] > key)
high = mid - 1;
else
low = mid + 1;
}
return -1;
}
**3.实验比较:设计并产生实验测试数据,考察比较两种查找方法的时间性能, 并与理论结果进行比较。以下具体做法可作为参考: **
(1) 第 1 组测试数据: n=1024 个已排序的整数序列(如 0 至 2048 之间 的奇数);第 2 组测试数据:第 1 组测试数据的随机序列。
生成测试数据:
#include<iostream>
#include<fstream>
#include<cstdlib>
#include <time.h>
using namespace std;
#define max 2048
//生成0到2048的奇数序列
int main() {
ofstream ofs1("input1.txt");
int data[max];
int total = 0;
for (int i = 0; i <= max; ++i) {
if (i % 2) {//第一组。有序奇数序列
data[total++] = i;
ofs1 << i << " ";//用空格、制表符、或者换行来分割数据,方便输入流读取
}
}
ofs1.close();
//第二组。无序奇数序列,把data打乱再输出就行了
ofstream ofs2("input2.txt");
srand((int)time(NULL));//随机数种子
for (int i = 0; i < total; i++) {
int index = rand() % total;
swap(data[i], data[index]);
}
while (total--) {
ofs2 << data[total] << " ";
}
ofs2.close();
}
输出结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5scLLRdd-1677332245602)(2022120067-刘虹志-作业5.assets/IL%RVW92PBRB{CFR[0M94_W.png)]
**(2) 以上述两组测试数据作为输入,分别建立 BST 查找结构。 **
建立代码
BST T1("input1.txt");
BST T2("input2.txt");
文件存放位置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-exVtMA9D-1677332245603)(2022120067-刘虹志-作业5.assets/图片1.png)]
**(3) 编写程序计算所建的两棵 BST 的查找成功和查找失败的平均查找长度(主要是改造 Search 算法,对“比较”进行计数),并与理论结 果比较。 **
n=1024 个已排序的整数序列测试代码;
BST T1("input1.txt");
T1.search_cost();
实验结果:
n=1024 个已排序的整数序列
实际结果为
查找成功的平均查找长度为:512.5
查找失败的平均查找长度为:512.999
与理论结果相符合
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZpSqMkrK-1677332245603)(2022120067-刘虹志-作业5.assets/图片2.png)]
n=1024 个随机的整数序列测试代码;
BST T2("input2.txt");
T2.search_cost();
实验结果:
n=1024 个随机的整数序列
实际结果为
查找成功的平均查找长度为:11.8633
查找失败的平均查找长度为:12.8507
与理论结果相符合
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YnqhBnc8-1677332245604)(2022120067-刘虹志-作业5.assets/[DW6%XYG}22C]EH4ZDQH7ST.png)]
(4) 分别以上述 BST 查找结构的中序遍历序列作为折半查找算法的输入,编写程序分别计算折半查找的查找成功和查找失败的平均查找长度,并与理论结果比较。
中序遍历测试代码:
BST T1("input1.txt");
cout <<endl<< "T1的中序序列" << endl;
T1.bst_sort(T1.T);
BST T2("input2.txt");
cout << endl << "T2的中序序列" << endl;
T2.bst_sort(T2.T);
结果:二者的中序序列完全相同
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2w0qMpIm-1677332245604)(2022120067-刘虹志-作业5.assets/3.png)]
前面已经证明顺序和乱序整数序列的中序输出一样,这里就只用计算一次平均查找长度
折半查找计算长度代码;
BST T1("input1.txt");
int sl = 0;//查找成功总长度
int s = 0;//查找成功元素个数
int fl = 0;//查找失败总长度
int f = 0;//查找失败元素个数
for (int i = 0; i <= 2048; ++i) {
if (i % 2) {//奇数
s++;
Bin_Search(T1.f_data, T1.total - 1, i, sl);
}
else {//偶数
f++;
Bin_Search(T1.f_data, T1.total - 1, i, fl);
}
}
double len_s = (double)sl / s;
double len_f = (double)fl / f;
cout << "查找成功的平均查找长度为:" << len_s << endl;
cout << "查找失败的平均查找长度为:" << len_f << endl;
折半查找结果:
实际结果
查找成功的平均查找长度为:9.01172
查找失败的平均查找长度为:10.002
与最好情况下的折半查找理论长度基本相同
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mo7Zl9DF-1677332245604)(2022120067-刘虹志-作业5.assets/4.png)]
(5) 以上实验能否说明:就平均性能而言,BST 的查找与折半查找差不多,为什么?
在随机数组情况下,BST的平均查找长度和折半查找差不多,时间效率都为O(logn)。
在有序数组的情况下,BST可能会形成一颗很长的右斜树或者左斜树,这时的时间效率就就和随机情况下差距很大了。
ouble)sl / s;
double len_f = (double)fl / f;
cout << “查找成功的平均查找长度为:” << len_s << endl;
cout << “查找失败的平均查找长度为:” << len_f << endl;
折半查找结果:
实际结果
查找成功的平均查找长度为:9.01172
查找失败的平均查找长度为:10.002
与最好情况下的折半查找理论长度基本相同
[外链图片转存中...(img-mo7Zl9DF-1677332245604)]
**(5) 以上实验能否说明:就平均性能而言,BST 的查找与折半查找差不多,为什么?**
在随机数组情况下,BST的平均查找长度和折半查找差不多,时间效率都为O(logn)。
在有序数组的情况下,BST可能会形成一颗很长的右斜树或者左斜树,这时的时间效率就就和随机情况下差距很大了。
但是实际上有序数组很少出现,考虑大部分情况,可以认为平均性能上,BST的查找与折半查找差不多。