数据结构实验——查找实验

一实验内容

编写算法实现下列问题的求解。

(1) 对下列数据表,分别采用二分查找算法实现查找,给出查找过程依次所比较的元素(的下标),并以二分查找的判定树来解释。   

第一组测试数据:

数据表为 (1,2,3,4,6,7,8,9,10,11,12,13,17,18,19,20,24,25,26,30,35,40,45,50,,100)

查找的元素分别为: 2,8,20,  30,50,5,15,33,110   

第二组数据:

数据表为 (2,3,5,7,8,10,12,15,18,20,22,25,30,35,40,45,50,55,60, 80,100)

查找的元素分别为: 22,8,80,3,100,1,13,120

(2) 设计出在二叉排序树中插入结点的算法,在此基础上实现构建二叉排序树的算法。 

测试数据:构建二叉排序树的输入序列如下:   

第一组数据:

100,150,120,50,70,60,80,170,180,160,110,30,40,35,175   

第二组数据:

100,70,60,80,150,120,50,160,30,40,170,180,175,35

(3) 设计算法在二叉排序树中查找指定值的结点。    

测试数据:在任务(1)中第一组测试数据所构造的二叉排序树中,分别查找下列元素:    150,70,160,190,10,55,175

(4) 设计算法在二叉排序树中删除特定值的结点。    

测试数据:在任务(1)中第一组测试数据所构造的二叉排序树中,分别删除下列元素:30,150,100

(5) 已知整型数组A[1..26]递增有序,设计算法以构造一棵平衡的二叉排序树来存放该数组中的所有元素。    

测试数据:数组元素分别为:

第一组数据:

(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26)

第二组数据:

(1,3,6,10,15,21,28,36,45,55,66,78,91,105,120,136,153,171,190,210,231,253,277,302,328)

二、算法设计

(除书上给出的基本运算(这部分不必给出设计思想),其它实验内容要给出算法设计思想

4.1采用二分查找算法实现查找。

前提:有序数组中查找关键词所在的位置

首先确定整个查找区间的中间位置 mid = low+(high-low)/2

用待查关键字key值与中间位置的关键字值进行比较;

若相等,则查找成功

若key小于a[k],则在前(左)半个区域继续进行折半查找;

若key大于a[k],则在后(右)半个区域继续进行折半查找。

对确定的缩小区域再按折半公式,重复上述步骤。

另外,若不存在,则提示即可。

4.2在二叉排序树中插入结点。

首插入结点也要保持二叉排序树有序。

首先若树为空,直接将其作为根节点。

若不为空,且值大于当前比较的结点,那么向当前结点右孩子方向继续比较,寻找插入位置。

若值小于当前比较的结点,那么向当前左孩子方向继续比较,寻找插入位置。

4.3在二叉排序树中查找指定值的结点。

先判断当前节点是否为要查找的节点,相等就返回当前节点,作为递归中值的条件。

如果不相等,且左子树节点值大于key,则递归左子树查找。

如果左递归先序查找,找到该节点,那就返回该节点。

如果不相等,且右子树节点值小于key,则递归右子树查找。

如果右递归先序查找,找到该节点,那就返回该节点。

若未找到,return NULL。

4.4二叉排序树中删除特定值。

二分三种情况讨论:

1)要删除的结点为叶子结点时,直接删除,调整父结点的删除了的孩子为NULL;

2)要删除的结点只有一个孩子时候,删除当前结点,并且将要删除结点的孩子赋给要删除的结点的父结点即可。

3)要删除的结点有两个孩子时,这里采取顶替法,要寻找到要删除的结点的前驱结点,将值赋给要删除的结点,然后删除这个前驱结点,并且做适当的调整。删除的结点的前驱结点一定是没有右孩子的。找到之后将值赋给p,将其父结点的右孩子设置为自己的左孩子。其中还有一个特殊情况,当sf==p时,此时p的前驱结点就是自己的左孩子,将p的左孩子设置为s的左孩子即可。

4.5构造一棵平衡的二叉排序树。

(1)首先,平衡二叉树又称为AVL树,它或者是一棵空树,或者是有下列性质的二叉树:它的左子树和右子树都是平衡二叉树,且左右子树的深度之差的绝对值不超过1。二叉树的的平衡因子BF为:该结点的左子树的深度减去它的右子树的深度,则平衡二叉树的所有结点的平衡因子为只可能是:-1、0和1。

(2)在一棵二叉查找树中插入结点后,调整其为平衡二叉树。若向平衡二叉树中插入一个新结点后破坏了平衡二叉树的平衡性。首先要找出插入新结点后失去平衡的最小子树根结点的指针。然后再调整这个子树中有关结点之间的链接关系,使之成为新的平衡子树。当失去平衡的最小子树被调整为平衡子树后,原有其他所有不平衡子树无需调整,整个二叉排序树就又成为一棵平衡二叉树。调整方法如下:

插入点位置必须满足二叉查找树的性质,即任意一棵子树的左结点都小于根结点,右结点大于根结点

找出插入结点后不平衡的最小二叉树进行调整,如果是整个树不平衡,才进行整个树的调整。

平衡二叉树要保证平衡因子始终小于二,大于二时就要做出调整。

(3)调整方式具体有四种,

RR、LL、LR和RL

RR是将最下面的不平衡的结点的右节点作为其父结点的孩子,这个不平衡的结点作为他孩子的左孩子,他原本右孩子的左孩子变成它的右孩子。

LL 是将最下面的不平衡的结点的左节点作为其父结点的孩子,这个不平衡的结点作为他孩子的右孩子,他原本左孩子的右孩子变成它的左孩子。

RL 可以先对他的右孩子做一次LL调整,在对他自己做一下RR调整

LR 可以先对他的左孩子做一次RR调整,在对他自己做一下LL调整。

#include "AVLTree.h"
#define MaxLen 100 
typedef int elementType;

//二分查找 
int bin_search(elementType a[], int n, elementType key)
{
	int low = 0, high = n - 1,mid;
	while (low <= high)
	{
		mid = (low + high) / 2;
		if (key < a[mid])
		{
			high = mid - 1;
		}
		else if (key > a[mid])
		{
			low = mid + 1;
		}
		else
		{
			return mid;//查找成功,return mid;
		}
	}
	return -1;//查找失败
}

//插入 
void insert(btNode*& T, btNode* S)
{
	if (T == NULL)
	{
		T = S;
	}
	else if ((T)->data < S->data)
	{
		insert(T->rChild, S);

	}
	else
	{
		insert(T->lChild, S);

	}
}

//创建树 
void creatTree(btNode*& T)
{
	cout << "请输入一个数,输入-9999停止创建" << endl;
	int a;
	cin >> a;
	if (a != -9999)
	{
		T = new btNode();
		T->data = a;
		T->lChild = NULL;
		T->rChild = NULL;
	}
	cin >> a;
	while (a!=-9999)
	{
		btNode* S = new btNode();
		S->data = a;
		S->lChild = NULL;
		S->rChild = NULL;
		insert(T, S);
		cin >> a;
	}
	cout << "中序遍历为" << endl;
	inTraverse(T);
}

//查找结点 
btNode* search(btNode* t, elementType key)
{
	if (t)
	{
		if (t->data == key)
			{
				cout << "查找成功" << endl;
				return t;
			}
			else if (t->data < key)
			{
				return search(t->rChild, key);
			}
			else
			{
				 return search(t->lChild , key);
			}
	}
	cout << "没有该节点" << endl;
	return NULL;
	
}

void deletekey(btNode*& Root, elementType num)
{
	              //输入要删除的结点的值
	btNode *temp, *previous;
	temp = Root;
	previous = Root;
	while (temp != NULL)
	{
		if (temp->data == num)
			break;
		else if (temp->data > num)
		{
			previous = temp;
			temp = temp->lChild;
		}
		else
		{
			previous = temp;
			temp = temp->rChild;
		}
	}
	if (temp != NULL)
	{
		if (temp->lChild == NULL)        //要删的结点的左孩子为空的情况
		{
			if (temp==Root && temp->rChild==NULL)
			{
			    delete temp;
			    temp=NULL;
			}
			else
			{
			    previous->lChild==temp ? previous->lChild=temp->rChild :previous->rChild=temp->rChild;
                delete temp;
			}
		}
		else if (temp->rChild == NULL)        //要删的结点的右孩子为空的情况
		{
		    previous->lChild == temp ? previous->lChild=temp->lChild :previous->rChild=temp->lChild;
            delete temp;
		}
		else                            //要删的结点的左、右孩子都不为空的情况
		{
			btNode *right_min = temp->rChild;
			previous = right_min;
			while (right_min->lChild != NULL)        //找到右子树最小结点
			{
			    previous = right_min;
				right_min = right_min->lChild;
			}
			temp->data = right_min->data;            //最小结点的值赋给要删除的结点
			if(right_min == previous)
            {
                temp->rChild=right_min->rChild;
            }
            else
            {
                previous->lChild = right_min->rChild;
            }
			delete right_min;                //删除右子树的最小结点
		}
	}
}

void CreateAVL(btNode*& T,elementType a[],int n)
{
	T = NULL;      //初始化二叉排序树为空树
	
	//循环读入数据,创建结点,插入到平衡二叉树中
	int i = 0;
	while (i<n)
	{
		insertNode(T, a[i]);   //平衡二叉树插入结点
		i++;
	}
}

//初始化数组
void initArray(int a[]){
	int n;
	cout << "请输入数组大小:";
	cin >> n;
	cout << "请输入元素:\n";
	for (int i = 0; i < n; i++){
		cin >> a[i];
	}
	int x2;
	cout << "第一题,输入需要查找数" << endl;
	cin >> x2;
	int pos = bin_search(a, n, x2) + 1;
	if (pos == 0)
	{
		cout << "该元素不存在!" << endl;
	}
	else{
		cout << "位置为:" << pos;
	}
}

///
///
///创建平衡树
///
///
//平衡二叉树定义和运算实现
# include<iostream>
using namespace std; 
typedef int elementType;


typedef struct avlNode
{
	elementType data;
	int bf;
	struct avlNode *lChild,*rChild;
}btNode, *AvlTree;


//LL型调整
btNode* LL(btNode* A, btNode* B)  //返回调整后的子树根结点B
{
	A->lChild=B->rChild;   //B的右子树调整为A的左子树
	B->rChild=A;           //A为B的右孩子 
	A->bf=0;               //A的平衡因子为0
	B->bf=0;               //B的平衡因子为0

	return B;              //B为调整后的根结点
}

//LR型调整
btNode* LR(btNode* A, btNode* B)  //返回调整后的子树根结点指针
{
	btNode* C=B->rChild;   //C作为B的右子树
	A->lChild=C->rChild;   //C的右子树调为A的左子树
	B->rChild=C->lChild;   //C的左子树调为B的右子树
	C->lChild=B;           //B调为C的左子树
	C->rChild=A;           //A调为C的右子树
	   //更新平衡因子
	   //根据新结点插入到C的不同位置,对调整后A、B、C的平衡因子进行调整
	switch(C->bf)
	{
	case 1:  //新结点插入到了C的左子树上
		A->bf=-1;
		B->bf=0;
		break;
	case 0:  //最简单的LR型,即B的左子树空,新插入结点为C本身。
		A->bf=0;
		B->bf=0;
		break;
	case -1:  //新结点插入到了C的右子树上
		A->bf=0;
		B->bf=1;
		break;
	}
	C->bf=0;  //调整C的平衡因子

	return C;  //C为调整后的根结点
}


//RR型调整
btNode* RR(btNode* A, btNode* B)  //返回调整后的子树根结点指针
{
	A->rChild=B->lChild;   //B的左子树调整为A的右子树
	B->lChild=A;           //A为B的左孩子
	A->bf=0;
	B->bf=0;

	return B;              //B为调整后的根结点
}
//RL型调整
btNode* RL(btNode* A, btNode* B)
{
	btNode* C=B->lChild;  //C作为B的左子树
	A->rChild=C->lChild;  //C的左子树调为A的右子树
	B->lChild=C->rChild;  //C的右子树调为B的左子树
	C->lChild=A;          //A调为C的左孩子
	C->rChild=B;          //B调为C的右孩子
	    //更新平衡因子
	    //根据新结点插入到C的不同位置,对调整后A、B、C的平衡因子进行调整
	switch(C->bf)
	{
	case 1:  //新结点插入到了C的左子树
		A->bf=0;
		B->bf=-1;
		break;
	case 0:  //最简单的RL型,即B的右子树为空,新插入结点为C本身
		A->bf=0;
		B->bf=0;
		break;
	case -1:  //新结点插入到了C的右子树上
		A->bf=1;
		B->bf=0;
		break;
	}
	C->bf=0;  //调整C的平衡因子

	return C;  //C为调整后的根结点
}

//插入 
int insertNode(btNode *&T, elementType x)
{
	btNode *p,*s;
	btNode *A,*Af,*B;
	int d;
	if(T==NULL)   //空树,插入为根结点
	{
		T=new btNode;
		T->data=x;
		T->lChild=NULL;
		T->rChild=NULL;
		T->bf=0;
		return 1;
	}	
	
	Af=NULL;
	p=NULL;    //插入结点的父结点
	s=T;
	A=T;
	while(s)
	{
		if(s->data==x)  //关键字已经存在,插入失败,退出
			return 0;
		if(s->bf!=0)    //搜索插入点p
		{
			Af=p;
			A=s;
		}
		p=s;           //p为插入结点的父结点
		if(s->data>x)
			s=s->lChild;
		else
			s=s->rChild;	
	}

	s=new btNode;
	s->data=x;
	s->lChild=NULL;
	s->rChild=NULL;
	s->bf=0;

	if(p->data>x)
		p->lChild=s;   //新结点插入为p的左孩子
	else
		p->rChild=s;   //新结点插入为p的右孩子

	//新结点已经插入,A为最低不平衡子树根结点
	//d为标记,d=1:新结点插入到A的左子树;d=-1:新结点插入到A的右子树
	if(A->data>x)      //x插入到A的左子树上
	{
		B=A->lChild;   //B为A的左孩子
		p=B;
		d=1;
	}
	else               //x插入到A的右子树上
	{
		B=A->rChild;   //B为A的右孩子
		p=B;
		d=-1;
	}

	//修改结点B到新插入结点s路径上各结点的bf值
	while(p!=s)
	{
		if(p->data>x)   //p的左子树增高
		{
			p->bf=1;
			p=p->lChild;		
		}
		else             //p的右子树增高
		{
			p->bf=-1;
			p=p->rChild;	
		}	
	}

	if(A->bf==0)   //A原来bf为0,插入后不会失去平衡
	{
		A->bf=d;
		return 1;	
	}
	if(A->bf==-d)  //新结点插在较低子树上,A的平衡因子变为0
	{
		A->bf=0;
		return 1;
	}

	//新结点插入在较高子树上,失衡,需要调整
	if(d==1)       //新结点插入在A的左子树上
	{
		if(B->bf==1)
			B=LL(A,B);  //LL型调整
		else
			B=LR(A,B);  //LR型调整
	}
	else          //新结点插入在A的右子树上
	{
		if(B->bf==-1)
			B=RR(A,B);  //RR型调整
		else
			B=RL(A,B);  //RL型调整	
	}

	if(Af==NULL)
		T=B;   //原来的A为树根
	else
	{
		if(Af->lChild==A)
			Af->lChild=B;
		else
			Af->rChild=B;	
	}
}

//交互创建平衡二叉树算法开始------------------------------------------------------------------
void CreateAVLTree(btNode *&T)
{
	elementType x;	
	T=NULL;      //初始化二叉排序树为空树
	cout<<"请输入结点数据(整数,-9999退出):"<<endl;
	cout<<"x=";
	cin>>x;
	    //循环读入数据,创建结点,插入到平衡二叉树中,-9999退出
	while(x!=-9999)
	{		
		insertNode(T,x);   //平衡二叉树插入结点

		cout<<"x=";
		cin>>x;	
	}
}
//交互创建平衡二叉树算法结束------------------------------------------------------------------


//递归先序遍历
void preTraverse(btNode *T)
{
	if(T)
	{
		cout<<T->data<<" ";         //访问根结点。打印当前结点元素值,替代visit(T)函数
		preTraverse(T->lChild);     //先序遍历左子树
		preTraverse(T->rChild);     //先序遍历右子树
	}
}
//中序遍历--递归
void inTraverse(btNode *T)
{
	if(T)
	{		
		inTraverse(T->lChild);      //中序遍历左子树
		cout<<T->data<<" ";         //访问根结点。打印当前结点元素值,替代visit(T)函数
		inTraverse(T->rChild);      //中序遍历右子树
	}
}
//后序遍历--递归
void postTraverse(btNode *T)
{
	if(T)
	{		
		postTraverse(T->lChild);      //后序遍历左子树		
		postTraverse(T->rChild);      //后序遍历右子树
		cout<<T->data<<" ";         //访问根结点。打印当前结点元素值,替代visit(T)函数
	}
}

//二叉排序树删除结点算法开始----------------------------------------------------------------
int delNode(btNode *&T, elementType x)
{
	btNode *p,*s,*pf;   //p指删除结点,pf指p的父结点
	
	if(T==NULL)
		return 0;       //空树删除失败
	  //搜索待删除结点p,及其父结点指针pf
	p=T;
	pf=T;
	while(p)
	{		
		if(p->data==x)   //找到目标结点,退出循环
			break;
		else if(p->data>x)  //搜索左子树
		{
			pf=p;
			p=p->lChild;
		}
		else                //搜索右子树
		{
			pf=p;
			p=p->rChild;		
		}	
	}
	if(p==NULL)   //目标结点不存在,删除失败
		return 0;
	    //下面开始删除结点p,其父结点为pf
	if(p->lChild==NULL)   //p没有左子树,或叶子结点,用右子树的根结点替代p
	{
		if(pf==p)   //删除的是根结点
			T=p->rChild;
		else
		{
			if(pf->lChild==p)
				pf->lChild=p->rChild;
			else
				pf->rChild=p->rChild;		
		}
		delete p;	
	}
	else if(p->rChild==NULL)  //p没有右子树
	{
		if(pf==p)   //删除的是根结点
			T=p->lChild;
		else
		{
			if(pf->lChild==p)
				pf->lChild=p->lChild;
			else
				pf->rChild=p->lChild;		
		}
		delete p;		
	}
	else    
	{       //顶替法1
		    //p的左、右子树皆不空,p的中序直接前驱替代,即p左子树最大值结点替代
		pf=p;		
		s=p->lChild;
		while(s->rChild)
		{
			pf=s;			
			s=s->rChild;
		}
		p->data=s->data;
		if(pf==p)  //s为p(pf)左子树的根结点,且没有右子树,s是p(pf)的直接前驱。将s的左子树接续为p的左子树。
			pf->lChild=s->lChild;
		else
			pf->rChild=s->lChild;
		delete s;	
	}	
	return 1;
}

//二叉排序树删除结点算法开始----------------------------------------------------------------

//销毁二叉树
void DestroyBt(btNode *&T)
{
	if(T)
	{
		DestroyBt(T->lChild);   //递归销毁左子树
		DestroyBt(T->rChild);   //递归销毁右子树
		delete T;               //释放根结点
		T=NULL;
	}
}

//求二叉树高度(深度)
int btHeight(btNode *T)
{
	int lh,rh;
	if(!T)
		return 0;
	else
	{
		lh=btHeight(T->lChild);   //求左子树高度
		rh=btHeight(T->rChild);   //求右子树高度
		//return (lh>rh?lh:rh)+1;  //简略写法
		if(lh>rh)
			return lh+1;
		else
			return rh+1;
	}
}

//设置平衡因子bf--测试删除算法
void setBf(btNode *&T,elementType x)
{
	btNode *p;
	
	if(T==NULL)
		return;
	p=T;
	while(p)
	{
		p->bf=btHeight(p->lChild)-btHeight(p->rChild);
		if(p->data>x)
			p=p->lChild;
		else
			p=p->rChild;	
	}
}

//
//
//主函数部分
///
//
#include<iostream>
#include "AVL.h"
#include <stdlib.h>
using namespace std;


int main() {
	int choice;
	int x0;
	int a[MaxLen];
	cout << "请先创建一个二叉树:" << endl; 
	btNode* T1;
	btNode* t = NULL;
	//1 2 3 4 6 7 8 9 10 11 12 13 17 18 19 20 24 25 26 30 35 40 45 50 100 -9999
	creatTree(T1);
	cout << endl;
	while (1) {
		cout << "***************************必做***************************" << endl;
		cout << "1.采用二分查找算法实现查找。" << endl;
		cout << "2.在二叉排序树中插入结点" << endl;
		cout << "3.在二叉排序树中查找指定值的结点" << endl;
		cout << "4.二叉排序树中删除特定值" << endl;
		cout << "5.构造一棵平衡的二叉排序树。" << endl;
		
		cout << "6.退出。" << endl;
		cout << "***************************结束***************************" << endl;
		cout << "输入你所需要验证的题号:" << endl;
		cin >> choice;
		switch (choice)
		{
		case 6:
			return 0;
		case 1:
			initArray(a);

			system("pause");
			system("cls");

			break;
		case 2:
			btNode* S ;
			int x;
			// 100 150 120 50 70 60 80 170 180 160 110 30 40 35 175 -9999
			cout << "\n请输入要插入结点:";
			cin >> x;
			S = new btNode;
			S->data = x;
			S->lChild = S->rChild = NULL;
			insert(T1, S);
			cout << "\n插入成功!中序遍历:\n";
			inTraverse(T1);
			system("pause");
			system("cls");
			break;
		case 3:
			cout << "输入要查找的值" << endl;
			int x1;
			cin >> x1;

			t = search(T1, x1);
			if (t != NULL)
			{
				if (t->lChild == NULL)
				{
					cout << "无左孩子" << endl;
				}
				else
					cout << "左孩子为:" << t->lChild->data << endl;
				if (t->rChild == NULL)
				{
					cout << "无右孩子" << endl;
				}else
				cout << "右孩子为:" << t->rChild->data << endl;
			}

			system("pause");
			system("cls");
			break;
		case 4:
			cout << "请输入要删除数据:" << endl;
			int x2; 
			cin >> x2;
			deletekey(T1, x2);
			cout << "中序遍历为" << endl;
			inTraverse(T1);
			system("pause");
			system("cls");
			break;
		case 5:
			int b[26];
			for (int i = 0; i < 26; i++)
			{
				b[i] = i + 1;
			}
			btNode* T2;
			CreateAVL(T2, b, 26);
			cout << "中序遍历为" << endl;
			inTraverse(T2);
			system("pause");
			system("cls");
			break;
		default:
			break;
		}

	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值