数据结构实验4 特殊二叉树的应用

背景

静态查找表(Static Search Table):只作查找操作的查找表。
动态查找表(Dynamic Search Table):在查找过程同时插入查找表中不存在的数据元素,或者从查找表中删除已经存在的某个数据元素。

实验目的

基于课程“BST”,实现二叉查找树(BST),实现一个静态查找表。

基本要求

1.使用二叉查找树(BST)来实现。
2. 二叉树使用链式结构(二叉链表)实现。
3. 基于BST,实现静态查找表。
4. 在查找表中查找时,要输出关键字比较的次数。

需求分析

0问题描述

给定一个含有n个整数的集合,有一查找系统:用户可随意给出一个整数m,便可 通过该系统知道m是否在这个集合中,且可以知晓查找该整数m需要多少步。请你实 现该功能。

1.问题分析

实现的功能:
1.通过键盘输入整数的个数n以及n个整数还有查找的元素值m;
2.查找m是否在这n个整数中;
3.求出查找该整数m需要的步数;
4.通过屏幕显示是否查找成功以及比较的次数。

2.输入数据

第一行输入整数个数n,且0<n<=100;
第二行包含n个整数a1, a2, …, an,用空格分隔,a_1≠a_2≠⋯≠a_n,且-1000=<a1, a2, …, an<=1000;接下来的每一行都有一个整数m,作为待查元素值;最终以ctrl+Z结束输入。

3.输出数据

每当输入一个待查元素,就输出查找成功或不成功,并且后面有一个数字表示查找的次数,例如:
输入:21 //查找21,
输出:查找成功 1//表示查找时比较的次数。

4.测试样例设计

样例 1: 整数以升序排列(构建的树为斜树)
输入: 请输入数据个数: 4
请输入数据: 2 3 7 8
请输入欲查找的数:3
请输入欲查找的数:100
输出: 查找成功 2
查找失败 4

样例 2:集合中比第一个数小的数的个数大于比第一个数大的数的个数
输入: 请输入数据个数:5
请输入数据:8 4 10 6 3
请输入欲查找的数:4
请输入欲查找的数:10
请输入欲查找的数:2
输出 查找成功 2
查找成功 2
查找失败 3

样例 3:按照BST特性可构建出完全二叉树
输入: 请输入数据个数:5
请输入数据:20 9 30 7 25
请输入欲查找的数:25
输出: 查找成功 3

样例 4: 只有一个整数
输入: 请输入数据个数:1
请输入数据:2
请输入欲查找的数:0
请输入欲查找的数:2
输出: 查找成功 1
查找失败 1

样例 5:第一个数为这组数中最小的数
输入: 请输入数据个数:7
请输入数据:1 6 7 78 41 54 4
请输入欲查找的数:41
请输入欲查找的数:85
输出: 查找成功 5
查找失败 4

二、概要设计

1.抽象数据类型

数据对象:一组互不相等且可比较大小的整数
数据关系:空树或者有BST特性的二叉树
基本操作:
1.准备能储存这组数据的空间
2.插入元素
3.访问左子树
4.访问右子树
5.查找元素

BST的ADT的设计:

ADT  BSTtree{ 
数据对象:D={pi|pi∈整数,i=1,2,... ,n,n∈整数}
数据关系:R
若D = Φ,则R=Φ;
若D≠Φ,则R={H},H是如下二元关系:
	在D中存在唯一的称为根的数据元素root,它在关系H下无前驱;
	若D-{root}≠Φ,则存在D-{root}={Dl,Dr},且Dl∩Dr=Φ,xl<root<xr;
	若Dl≠Φ,则Dl中存在惟一的元素x1,<root,x1>∈H,且存在Dl上的关系Hl∈H; 若Dr≠Φ,则Dr中存在惟一的元素xr,<root,xr>∈H,且存在Dr上的关系Hr∈H;H={<root,x1>,<root,xr>,H1,Hr};
	(D1,{H1})是一棵符合本定义的二叉树,称为根的左子树; (Dr,{Hr})是一棵符合本定义的二叉树,称为根的右子树。
基本操作:
BSTNode* getroot();//操作功能:获得根节点,结果返回根节点
void inserthelp(BSTNode*, int &e );
//操作功能:插入值为e的结点
void  insert(E& e);//调用inserthelp函数,实现数据封装
bool  findhelp(BSTNode* ,int e,int &count=0);
//操作功能:查找值为e的结点
//若e存在,则返回查找成功,否则返回查找失败,count为比较的次数。
bool find( int e);
//调用findhelp函数,实现数据封装

class BSTNode{
private:
         int data;           //二叉树节点存储的值
         BSTNode*lc;
         BSTNode*rc;     //左右孩子指针
public:
         BSTNode() ;      //初始化节点      
         BSTNode(int e,BSTNode* l=NULL,BSTNode* r=NULL) ;
         E getData();
         BSTNode* left();
         BSTNode* right();
         void setLeft(BSTNode* b);
         void setRight(BSTNode* b);
};

2.	BST Tree ADT
int count=0;//记录关键字的比较次数
class BST:public BSTNode{
private:
         BSTNode*root;                   // 树的根节点
         int nodeCount;                    //结点个数
public:
         BST() ;  //初始化一棵空树
         BSTNode*  getRoot() ;//获得根结点
         BSTNode*  inserthelp(BSTNode*, const E&);
         void  insert(E& e);//插入
         bool  findhelp(BSTNode* ,int&, int & );
         bool find( int& e);//查找

BST的基本操作-find
问题描述:
对于给定的一棵BST和待查元素e,count作为计数器记录查找过程中比较的次数,若BST中存在某个结点的元素值等于e,结果返回计数器的值。否则则查找失败,结果返回计数器的值。
算法思想:
1.定义一个指向BST树节点的临时指针q,并将count重设为0;
2.从BST根节点开始,将其地址赋给q;
3.若q不等于空指针,取q指针所指向的值与待查找的值e进行比较,count+1;
4.若q指针所指向的值等于e,则查找成功,结束查找;
5.若q指针所指向的值小于e,则将q结点左孩子的指针赋给q;
6.若q指针所指向的值大于e,则将q结点右孩子的指针赋给q;
7.重复第三步,直至q等于空结点,则查找不成功,结束查找。
算法描述:
在这里插入图片描述
算法分析:该查找算法取决于结点被找到的深度,最佳情况是该结点在根节点被找到,时间复杂度是O(1);最差情况等于树的深度。对于有n个结点的二叉树,如果二叉树是平衡的,则最差时间复杂度是O(logn);如果该树是斜树,则最差时间复杂度是O(n)。
2.算法的基本思想
(1)先用二叉链表实现二叉查找树,将一组数据的值储存到结点中;
(2)通过自定义的查找的基本操作对元素进行查找,若值未找到,则比较的次数为树的深度。
3.程序的流程
建树模块:输入n个整数,并根据BST特性建一棵BST树;

输出模块:调用BST树的查找函数,判断是否能找到以及用计数器进行记录比较次数; 以find函数的结果作为输出的内容。

三、详细设计

1.物理数据类型

物理数据类型:每个结点都有一个value,为整形int;还有两个指针。
物理数据结构:为了方便BST树进行插入和查找,所以选用二叉链表。

BSTNode* getroot();//操作功能:获得根节点,结果返回根节点
{return root;}
BSTNode* inserthelp(BSTNode*root, int &val );
{//操作功能:插入值为val的结点
	{
         if(root==NULL)
                   return new BSTNode(val,NULL,NULL);
         if(val<root->getdata())
                   root->setLeft(inserthelp(root->left(),val));
         else
                   root->setRight(inserthelp(root->right(),val));
         return root;
}
void insert(int e){ root=inserthelp(root, e );}//插入值为e的元素
bool find(int e)
{
	return findhelp(root,e);
}

2.输入和输出的格式

输入格式:第一行输入整数个数n;
第二行包含n个整数a1, a2, …, an,用空格分隔;
接下来的每一行都有一个整数b;
输出格式:若该元素在树中,则输出查找成功,并输出比较次数;否则输出查找不成功与 比较次数。

3.算法的具体步骤

建树模块:

								void creat(BSTtree& tree,int n){
//n为输入的整数个数         				int i, n,val;  cin>>n;
										for(i=0;i<n;i++){
//输入这组整数          					cin>>val;                
	                       			 }
         								for(i=0;i<n;i++){
//将整数依次插入BST中             		tree.insert(A[i]);
         							 }
								}

输出模块:

								void print(BSTtree T,int e){
//调用基本函数find        			 if(T.find(e))
//如果函数返回值不为0            	 cout<<"查找成功 "<<” ”<< count<<endl;
 则可找到e,返回比较的次数        	 else
//否则返回0                          cout<<"查找不成功 "
比较次数为该BST树的深度               <<” ”<<count<<endl;
									}

4.算法的时空分析

建树模块:利用BST的基本操作中的插入函数,最差情况下其时间按复杂度是O(logn);
输出模块:利用BST的基本操作中的查找函数,最差情况下其时间按复杂度是O(logn);

四、调试分析

1.调试方案设计

调试目的:查看BST树基本操作insert函数与find函数内部语句执行顺序
样例:n=4,这组数为1,2,3,4,依次查找1,2,3,4,5

方案:在函数里面设置一个断点,添加查看,然后开始编译调试,按照提示一步一步操作,在断点处,点击下一步下一步,并在函数里面,添加查看指针所位置的值的变化。
调试步骤:
1.添加端点
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2.添加查看
在这里插入图片描述
3.编译调试
在这里插入图片描述
调试过程和结果,及分析
调试过程:
1)先输入整数个数与该组整数
在这里插入图片描述
2)语句进入插入函数,建立新结点,此时BSTnode *&t并没有报错
在这里插入图片描述
3)开始插入下一个整数,发现根节点依然为空,然后发现不管插入哪个数据,都是执 行的if(t==NULL)里面的语句。
在这里插入图片描述
4)接着开始进行查找,输入要查找的值。

5)发现每次都是直接返回false,也就是根节点一直为空。
在这里插入图片描述
实验结果:无论查找什么数据都是查找失败,比较0次,知道是insert函数出了问题。
在这里插入图片描述
分析:根据以上调试,可得知是insert函数出了问题,root作为实参,函数执行完树并 没有发生改变。
解决方案:让insert函数每次都返回参数BSTnode *t,然后令root=insert(t,e)。

五、测试结果

样例一:
在这里插入图片描述
理由:该组数据对象构成一棵斜树,3是第二个元素,比较2次;100不能被找到,会 遍历该整棵树,比较4次。

样例二:
在这里插入图片描述
理由:4是根节点左子树结点,因此要比较2次;10是根节点右子树结点,因此比较2 次;2不能被找到,2小于8,所以依次与8,4,3比较,共比较3次。

样例三:
在这里插入图片描述
理由:该树为完全二叉树,25在尾部,共比较3次。

样例四:
在这里插入图片描述
理由:该树仅有根节点,所以都只会比较一次。

样例五:
在这里插入图片描述
理由:查找41需要依次与1,6,7,78,41比较,共比较5次;查找85需要依次查找 1,6,7,78共4次。

六、实验日志

2018-11-10:观看有关BST的视频,复习其特性与ADT的构建。

2018-11-11:撰写实验四预习报告。

2018-11-12:检查并修改实验报告,修改问题描述,将其改的更实用化。
在演算本上举例的时候,我发现查找的基本操作实现有误,原来的思路是:find函数最终返回一个整数,可找到的时候就返回比较的次数,找不到的时候就返回0。在主函数中,如果find返回0,深度就是其比较次数,但是最后我发现对于一些例子这是错误的。然后我将find函数改成了bool函数,里面加了一个参数count,并引用作为计数器。

2018-11-13:代码实现BST树与结点的ADT,对个别基本操作进行数据封装。

2018-11-14:代码实现主函数并运行。
第一次运行结果无论如何都是查找失败,比较次数是0,检查代码的时候发现insert函数写的不对,t->setleft(insert(t->left(),e))是正确的语句,我写成了insert(t->left(),e),反思了下,是自己对函数调用的理解不深。
而且在这个函数里我的参数写成了*&root,原意是在函数中就更改树的结构,所以我在公有成员中的函数insert里没有令root=insert(root,e),调试中发现root一直为空。 比较气人的是,dev里我对.h文件进行更改然后运行时,其实运行的一直都是相当于没更改前的代码,除非我在主函数中出现了一个大错误,然后我重新更改运行才是对的,这就导致我写成*&编译器也没有报错,不管我怎么修改代码结果也一直是错的,所以当天什么作业都没有写成。然后还想起上次写链表的时候也是结果不对,然后百度、一点点检查代码搞了白天一天加一晚上也不行,没想到三天过后我重新打开运行竟然就对了。

2018-11-15:整理完善最终实验报告。

附代码:

BSTnode.h

**BSTnode.h**
#ifndef _BSTNODE_H
#define  _BSTNODE_H

#include <iostream>

class BSTnode
{
	private:
		int date;
		BSTnode *lc;
		BSTnode *rc;
	public:
		BSTnode()//构造函数 
		{
			lc=rc=NULL;
		}
		BSTnode(int e,BSTnode *l=NULL, BSTnode *r=NULL)//含参构造函数
		{
			date = e;
			lc = l;
			rc = l;
		}
		int getdate()
		{
			return date;
		}
		BSTnode* left()
		{
			return lc;
		}
		BSTnode* right()
		{
			return rc;
		}
		void setleft(BSTnode *l)
		{
			lc = l;
		}
		void setright(BSTnode *r)
		{
			rc = r;
		}
};
#endif

BSTtree.h

#include "BSTnode.h"
#ifndef _BSTTREE_H
#define _BSTTREE_H

#include <iostream>

class BSTtree
{
	private:
		BSTnode *root;  //根节点
		BSTnode* insert(BSTnode *t, int e)//插入值为e的结点 
		{
			if (t == NULL)
			{
				t = new BSTnode(e, NULL, NULL);
				return t;
			}
			if (e < t->getdate())
			{
				t->setleft(insert(t->left(),e));
			}
			else if (e > t->getdate())
			{
				t->setright(insert(t->right(),e));
			}
			return t;
		}
		
		bool find(BSTnode *root, int e, int &count)
		{
			count=0;
			BSTnode *q = root;
			while (q)
			{
				count++;
				if (e == q->getdate())  return true; 
				else if (e < q->getdate()) 
						q = q -> left (); 
				else if (e > q->getdate()) 
		                q = q -> right (); 
			}
			return false;
		}
		void clear(BSTnode* t)	
		{
			if (t == NULL) return;
			clear(t->left());
			clear(t->right());
			delete t;
		}
	public:
		BSTtree()
		{
			root = NULL;
		} 
		BSTnode* getroot()
		{
			return root;
		}
		void clear()
		{
			clear(root);
		}
		void insert(int e)
		{
			root = insert(root,e);
		}
		bool find(int e,int &count)
		{
			return	find(root,e,count);
		}
};

#endif

main.cpp

#include "BSTnode.h"
#include "BSTtree.h"
#include <iostream>
using namespace std;

int main()
{
	BSTtree T;
	int n, a, count=0;
	cout << "请输入该组整数的个数:";
	cin >> n;
	cout << "请输入该组整数:";
	for (int i=0; i<n; i++)
	{
		cin >> a;
		T.insert(a); 
	} 
	int f;
	cout << "请输入要查找的值:" ;
	while (cin >> f)
	{
		if (T.find(f,count)) 
		{
			cout << "查找成功,共比较:" << count << "次" << endl; 
		}
		else 
		{
			cout << "查找失败,共比较:" << count << "次" << endl; 
		}
		cout << "请输入要查找的值:" ; 
	}
	T.clear();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值