1. 二叉树查找树算法实现
#include<stdio.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0
#define OVERFLOW -1
#define EQ(a,b) ((a)==(b))
#define LT(a,b) ((a)<(b))
typedef int ElemType;
typedef int Status;
typedef struct TElemType{
int key;
int num;
}TElemType;
typedef struct BiTNode{
TElemType data;
struct BiTNode *lchild,*rchild;
}BiTNode,*BiTree;
Status SearchBST(BiTree T,int key,BiTree f,BiTree &p){
if(!T) { p=f; return ERROR;}
else if(EQ(T->data.key,key)) { p=T; return OK; }
else if(LT(key,T->data.key))
return SearchBST(T->lchild,key,T,p);
else return SearchBST(T->rchild,key,T,p);
}
Status SearchBST1(BiTree T,int key,BiTree f,BiTree &p){
if(!T) { p=f; return ERROR;}
else if(EQ(T->data.key,key)) {
printf("%d %d",p->data.key,p->data.num);
p=T; return OK; }
else if(LT(key,T->data.key))
return SearchBST(T->lchild,key,T,p);
else return SearchBST(T->rchild,key,T,p);
}
Status InsertBST(BiTree &T,TElemType e){
BiTree s,p;
if(!SearchBST(T,e.key,NULL,p)){
s=(BiTree)malloc(sizeof(BiTNode));
s->data=e;
s->lchild=s->rchild=NULL;
if(!p) T=s;
else if(LT(e.key,p->data.key)) p->lchild=s;
else p->rchild=s;
return OK;
}
}
Status Output(TElemType e){
printf("%d ",e.key);
printf("%d\n",e.num);
}
Status PreOrderTraver( BiTree T){//二叉树先序遍历
if(T==NULL) return ERROR;
else{
Output(T->data);
PreOrderTraver(T->lchild);
PreOrderTraver(T->rchild);}
return OK;
}
int main()
{
BiTree T,f=NULL,q;
TElemType a;
int i,n,b;
printf("请输入你要创建的二叉排序树的结点个数:\n");
scanf("%d",&n);
for(i=0;i<n;i++){
scanf("%d",a.key);
scanf("%d",a.num);
InsertBST(T,a);
}
printf("请输入你要查找的关键字: ");{
scanf("%d",b);
if(SearchBST1(T,b,f,q)) printf("查找成功!\n");
else printf("查找失败!\n");}
printf("二叉树的先序遍历:\n");
PreOrderTraver(T);
system("pause");
return 0;
}
这个就是!希望可以帮助你!
2. 实验四 二叉排序树查找
#include<iostream.>
using namespace std;
typedef int KeyType;
typedef struct tree//声明树的结构
{
struct tree *left; //存放左子树的指针
struct tree *right; //存放又子树的指针
KeyType key; //存放节点的内容
} BSTNode, * BSTree; //声明二叉树的链表
BSTree insertBST(BSTree tptr,KeyType key)// 在二叉排序树中插入结点
{ //若二叉排序树tptr中没有关键字为key的结点,则插入,否则直接返回
BSTree f,p=tptr; //p的初值指向根结点
while(p) //查找插入位置,循环结束时,p是空指针,f指向待插入结点的双亲
{
if(p->key==key) //树中已有key,无须插入
return tptr;
f=p; //f保存当前查找的结点,即f是p的双亲
p=(key<p->key)?p->left:p->right;
}
p=(BSTree )malloc(sizeof(BSTNode)); //生成新结点
p->key=key; p->left=p->right=NULL;
if(tptr==NULL) //原树为空,新插入的结点为新的根
tptr=p;
else
if(key<f->key)
f->left=p;
else
f->right=p;
return tptr;
}
BSTree createBST()//建立二叉树
{
BSTree t=NULL; //根结点
KeyType key;
cin>>key;
while(key!=-1)
{
t=insertBST(t,key);
cin>>key;
}
return t;
}
void inorder_btree(BSTree root)// 中序遍历打印二叉排序树
{
BSTree p=root;
if(p!=NULL){
inorder_btree(p->left );
cout<<" "<<p->key<<" ";
inorder_btree(p->right );
}
}
int searchBST(BSTree t,KeyType key)//查找
{
if(key==t->key)
return 1;
if(t==NULL)
return 0;
if(key<t->key)
return searchBST(t->left,key);
else
return searchBST(t->right,key);
}
BSTree deleteBST(BSTree tptr,KeyType key)//删除
{
BSTree p,tmp,parent=NULL;
p=tptr;
while(p)
{
if(p->key==key)
break;
parent=p;
p=(key<p->key)?p->left:p->right;
}
if(!p) return NULL;
tmp=p;
if(!p->right&&!p->left) /*p的左右子树都为空*/
{
if(!parent) //要删根,须修改根指针
tptr=NULL;
else if(p==parent->right)
parent->right=NULL;
else
parent->left=NULL;
free(p);
}
else if(!p->right) //p的右子树为空,则重接p的左子树
{
p=p->left;
if(!parent) //要删根,须修改根指针
tptr=p;
else if(tmp==parent->left)
parent->left=p;
else
parent->right=p;
free(tmp);
}
else if(!p->left) //的左子树为空,则重接p的左子树
{
p=p->right;
if(!parent) //要删根,须修改根指针
tptr=p;
else if(tmp==parent->left)
parent->left=p;
else
parent->right=p;
free(tmp);
}
else if(p->right&&p->left) //p有左子树和右子树,用p的后继覆盖p然后删去后继
{ //另有方法:用p的前驱覆盖p然后删去前驱||合并p的左右
parent=p; //由于用覆盖法删根,则不必特殊考虑删根
p=p->right;
while(p->left)
{
parent=p;
p=p->left;
}
tmp->key=p->key;
if(p==parent->left)
parent->left=NULL;
else
parent->right=NULL;
free(p);
}
return tptr;
}
int main()
{
KeyType key;
int flag,test;
char cmd;
BSTree root;
do
{
cout<<"\n\n"<<endl;
cout<<"\t\t*******请选择你要执行的操作:********"<<endl;
cout<<"\n"<<endl;
cout<<"\t\t C.创建一棵二叉排序树\n";
cout<<"\t\t E.结束本程序\n";
cout<<"\n\n\t\t************************************"<<endl;
flag=0;
do
{
if(flag!=0)
cout<<"选择操作错误!请重新选择!\n";
fflush(stdin);
cin>>cmd;
flag++;
}while(cmd!='c'&&cmd!='C'&&cmd!='a'&&cmd!='A');
if(cmd=='c'||cmd=='C')
{
cout<<"请输入你所要创建的二叉树的结点的值,以-1结束:\n";
root=createBST();
do
{
flag=0;
cout<<"\n\n中序遍历二叉树:"<<endl;
inorder_btree(root);
cout<<"\n"<<endl;
cout<<"\t\t************请选择你要对这棵二叉树所做的操作:**************"<<endl;
cout<<"\t\t** **"<<endl;
cout<<"\t\t** S......查找你想要寻找的结点 **"<<endl;
cout<<"\t\t** I......插入你想要插入的结点 **"<<endl;
cout<<"\t\t** D......删除你想要删除的结点 **"<<endl;
cout<<"\t\t** Q......结束对这棵二叉树的操作 **"<<endl;
cout<<"\t\t** **"<<endl;
cout<<"\t\t***********************************************************"<<endl;
do{
if(flag!=0)
cout<<"选择操作错误!请重新选择!\n";
fflush(stdin);
scanf("%c",&cmd);
flag++;
}while(cmd!='s'&&cmd!='S'&&cmd!='i'&&cmd!='I'&&cmd!='d'&&cmd!='D'&&cmd!='q'&&cmd!='Q');
switch(cmd)
{
case 's':
case 'S':
cout<<"请输入你要查找结点的关键字:\n";
cin>>key;
test=searchBST(root,key);
if(test==0)
cout<<"\n对不起,你所查找的结点 "<<key<<"不存在!";
else
cout<<"\n成功找到结点\n"<<key<<" ";
break;
case 'i':
case 'I':
cout<<"请输入你要插入结点的关键字:\n";
cin>>key;
root=insertBST(root,key); //注意必须将值传回根
break;
case 'd':
case 'D':
cout<<"请输入你要删除结点的关键字:\n";
cin>>key;
root=deleteBST(root,key); //注意必须将值传回根
if(root==NULL)
cout<<"\n对不起,你所删除的结点 "<<key<<" 不存在!\n";
else
cout<<"\n成功删除结点 "<<key<<" ";
break;
}
}while(cmd!='q'&&cmd!='Q');
}
}while(cmd!='e'&&cmd!='E');
return 0;
}
3. 二叉树算法是什么
二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。
二叉树的第i层至多有2^(i 1)个结点;深度为k的二叉树至多有2^k 1个结点;对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0 = n2 + 1。二叉树算法常被用于实现二叉查找树和二叉堆。
二叉树是每个节点最多有两个子树的有序树。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。
(3)对分查找算法双叉树显示扩展阅读:
二叉树也是递归定义的,其结点有左右子树之分,逻辑上二叉树算法有五种基本形态:
1、空二叉树——(a)
2、只有一个根结点的二叉树——(b);
3、右子树为空的二叉树——(c);
4、左子树为空的二叉树——(d);
5、完全二叉树——(e)
注意:尽管二叉树与树有许多相似之处,但二叉树不是树的特殊情形。
4. 平衡二叉树的各种算法实现
多值结点平衡二叉树的结构及算法研究
1引言
传统的AV1.树是一种应用较为广泛的数据结构,适合”几组织在内存中的较小索引.它的
每个结l从上存储有一个关键字、一个平衡因子和两个指针项,山”几它是一棵接近”几理想状态的
平衡二叉树,所以AV1.树具有很高的查询效率.但正如任何事物都具有两而性一样,AV1.树同
样存在比较严重的缺l从,一是存储效率比较低:真正有用的关键字在结l从上所,片的空间比例较
小,而作为辅助信息的平衡因子和指针却,片据较大的空间;二是额外运算量比较大:当有结l从
被插入或删除而导致AV1.树不平衡时,AV1.树就需要进行调整而保持它的平衡性,山”几每个
结l从上只有一个关键字,所以任何一次的数据插入或删除都有可能导致AV1.树的平衡调整,
这种频繁的调整运算将大大降低AV1.树的存取效率.为解决以上问题,结合T3树每个结l从可
以存储多个关键字项的优l侧}l,木文提出了多值结l从平衡二叉树(简称MAV1.树),它的主要特
点在”几每个MAV1.树的结l从都存储有多个关键字项,而其它信息仍与AV1.树一样,即一个平
衡因子和两个指针项.
2 MAV1.树结构描述
MAV1.树仍旧是一种平衡二叉树,它的整体树型结构和算法也是建立在传统的平衡二叉
树基础之上的.MAV1.树的特征在”几它的每个结l从都可以存储多个关键字(较理想的取值大约
在20} 50个之间).用C++语言描述的MAV1.树结l从结构如卜:
struct NodeStruct
int IJ1emsOnNode;
int bf:
struct NodPStruct*lch;ld:
//一结点中项的数目
//平衡因子
//夕.子
struct NodeStruct * rchild:
}lemType }lemsi Max}lem} ;//结点中的项数组
Node T:
在这种结构中.ElemsOnNode反映的是“当前状态卜”该结l从中关键字项的个数.当在此结
点插入一个关键字时.FlemsOnNode值加1.当删除一个关键字时.则FlemsOnNode值减1.每个
结l从上可存储的关键字个数介J几1 } M axElem之间.bf为平衡因r.其作用等同J几AV1.树的平
衡因r. MAV1.树的任一结l从的平衡因r只能取一1 ,0和1.如果一个结l从的平衡因r的绝对
值大”几1.则这棵树就失去了平衡.需要做平衡运算保持平衡.lehild和:child分别为指向左右
J"树根结0的指针.Flems[ i]为结0中第i个关键字项.Flems} MaxFlem”是一个按升序排列的
关键字数组.具体的MAV1.树结l从结构如图1所示.
}lemsOnNode一h‘一* leh;ld一
图1
reh击3
}lemsi 0}一
树结点结构
}lemsi Max}lem}
MAVT
MAV1.树的结构特l从使它比AV1.树具有更高的存储效率.在AV1.树或MAV1.树中.实际
有用的信急只有关键字.1f1! ElemsOnNode ,bf ,lehild和:child都是为了构建树型结构If1J不得不添
加的辅助信急. MAV1.树就是通过减小这些辅助信急的比例来获得较高的存储效率.山MAV1.
树结l从的定义可以看出:FlemsOnNode和bf为int型.各,片4个字节长度.指针型的lchild和
rchild也各,片4个字节长度.在以上四项信急中.AV1.树结l从除了没有ElemsOnNode外.其余和
MAV1.树相同.现假设关键字长度为24字节.M axFl二值定为50.则对AV1.树来说.它的结l从
长度为36字节.其中辅助信h,长度为12字节;If}J MAV1.树的结l从长度是1. 2K字节.其中辅助
信急长度为16字节.山此可以看出.MAV1.树在存储时.结l从中辅助信急长度,片整个结l从长度
的比例是很小的.它对存储空间的利用效率比 AV1.树要高.这一l从对”几主要而向内存应用的
MAV1.树来说是非常重要的.
在实际的应用中.当MAV1.树作为数据库索引结构时.为进一步节约内存空间.结l从中Fl-
emType的结构可根据实际需要作不同的定义.
( 1)当排序关键字较短时.可以直接将数据库中的关键字值拷贝到索引文件中.这样
MAV1.树既有较快的运行速度又不会,片用太大的空间.此时ElemType定义如卜
struct IdxRlemStruct
{
int RecPos://金己录号
KeyType Key://关键字
}R1emType;
( 2}当排序关键字较长时.如果直接将数据库中的关键字值拷贝到索引文件中会,片据较大
的空间.此时可以采用只存储关键字地址的形式.这样不管关键字有多长.映射到MAV1.树后
都只,片据一个指针的固定长度.这种以时间换空间的方法比较适合内存容量有限的情况.此时
ElemType定义如卜
struct Tdxl?lemStruct
int RecPos:
char * Key
R1emType;
//记录号
//关键字指钊
3基于MAUI.树的运算
MAUI.树的基木运算.包括MAUI.树的建立、记录的插入、删除、修改以及查询.这些算法
与基J几AVI.树的算法相似.都建立在一叉查询和平衡算法基础上.
3. 1 MAVI,树的平衡运算
如果在一棵原木是平衡的MAUI.树中插入一个新结l从.造成了不平衡.此时必须调整树的
结构.使之平衡化“21 .MAUI.树的平衡算法与AVI.树的平衡算法是相同的.但山J几MAUI.树的
每个结l从中都存储有多个关键字.所以在关键字个数相同的情况卜. MAUI.树的应用可以大大
减少平衡运算的次数.例如.假设具有n个关键字的待插入序列在插入过程中有5%(根据随
机序列特l从的不同.此数值会有所差异.这里以比较保守的5%为例)的新产生结l从会导致一
叉树出现不平衡.对AVI.树来说.山”几需要为每个关键字分配一个结l从.所以在整个插入过程
中做平衡的次数为n * 5%;对J几MAUI.树.设MAUI.树中M axFl二的值被定义为k(k为大J几1
的正整数少,则平均每k次的数据插入才会有一个新结l从产生,所以在整个插入过程中需做平
衡的次数仅为(nlk) * 5%.即在M axFl二取值为k的情况卜.对”几相同的待插入关键字序列.
在插入过程中MAUI.树用J几平衡运算的开销是AVI.树的1/ k.
3. 2数据查找
在MAUI.树上进行查找.是一个从根结l从开始.沿某一个分支逐层向卜进行比较判等的过
程.假设要在MAUI.树上查找的值为GetKey.查找过程从根结l从开始.如果根指针为NU1.1..则
查找失败;否则把要查找的值GetKey与根结l从关键字数组中的最小项Elems [ 0]进行比较.如
果GetKev小”几当前结i最小关键字.则递归查找左r树;如果GetKey'大”几Elems [ 0].则将
GetKey'与根结0关键字数组中的最大项Fletns} MaxFl二一1]进行比较.如果GetKey'大”几当前
结l从最大关键字.则递归查找右r树;否则.对当前结l从的关键字数组进行查找(山”几是有序序
列.可以采用折半查找以提高效率).如果有与GetKey'相匹配的值.则查找成功.返回成功信
息,7{报告查找到的关键字地址.
3. 3数据插入
数据插入是构建MAV1.树的基础.设要在MAV1.树*T上插入一个新的数据兀素GetKev,
其递归算法描述如卜:
(1)若*T为空树.则申清一新结} ' Elems} MaxElem}.将GetKey'插入到Flems[ 0]的位置.树
的深度增1.
(2)若*T未满.则在*T中找到插入位置后将GetKey'插入.JI在插入后保持结l从中的各
关键项有序递增.若己存在与GetKev相同的项.则不进行插入.
(3)如果*T为满结l从目一GetKey'值介”几Flems[ 0]和Flems} MaxFlem]之间.则在*T中找到
GetKev的插入位置posit ion.山”几*T木身就是满结l从.所以GetKev的插入必然会将原来*T中
的某个数据挤出去JI卜降到r树中.根据插入位置position的不同.分以卜几种情况处理:若*
T中存在与C etl} e`'相同的项.则不进行插入;若插入位置在*T结ii的前半部分(即position <
=MaxFlem/ 2).则将Flems[ 1]到Fletns} position”的数据依次左移一位.再把GetKey插入到Elems
} MaxFlem”中position的位置.Ifn原来*T中最左边项数据将被挤入到*T的左r树中.考察此
数据的特l从.它必然大”几*T左r树中的任一数据项.所以此时不需要作任何的额外运算.直
接将此数据插入到*T左r树根结i从的最右r孙位置处就可以了(见图2中插入,}} 11"后“1,>
的位置变化);若插入位置在*T结ii的后半部分(即position> MaxFlem/ 2).则将Fletns} posi-
tion}到Fletns} MaxFl二一2}的数据依次右移一位.再把GetKev插入到*T结0中position的位
置.与前一种情况类似.结l从中最右边被挤出的项将被插入到*T的右r树根结l从的最左r孙
的位置(见图2中插入“25"后" 30"的位置变化).
插入,"}i”插入”zs0
}o i is i }a
s}土 s
图2
满结点插入数据的过程
(4)若GetKey的值小”几T的最小项值.则将GetKey递归插入到T的左r树中.即在递归调
用时GetKey值不变Ifn T= T->lehild.
(5)若GetKey的值大”几T的最大项值.则将GetKey递归插入到T的右r树中.即在递归调
用时GetKey值不变Ifn T= T->rehild.
4结束语
山J几MAV1.树的结l从中存储有多个关键字值.所以它具有较高的存储效率;对MAV l树进
行查找是_分查找和顺序查找的结合.其查询效率只略低”几AV1.树.血山”几MAV1.树的平衡
运算比AV1.树要少得多.所以MAV1.树有很优秀的综合运算效率.综上所述.在数据量大、内
存容量相对较小、数据增删运算比较频繁的情况卜.用MAV1.树作为常驻内存的索引结构是一
种理想的选择.