一实验内容
编写算法实现下列问题的求解。
(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;
}