二叉查找树和二叉平衡查找树
一.二叉查找树
1.数据类型定义:
typedef int InfoType;typedef int KeyType;// 假定关键字类型为整数
typedef struct node// 结点类型
{
KeyType key;// 关键字项
InfoType otherinfo;// 其它数据域,InfoType视应用情况而定 下面
// 不处理它
struct node *lchild,*rchild; // 左右孩子指针
}BSTNode;typedef BSTNode *BSTree;//BSTree是二叉查找树的结点指针类型
2.构建根节点
BSTree CreateBST(void)
{
BSTree T = NULL;// 初始时T为空树
KeyType key;
scanf("%d",&key);// 读入一个关键字
while(key){// 假设key=0是输入结束标志
InsertBST(&T,key);// 将key插入二叉查找树T
scanf("%d",&key);// 读入下一关键字
}
return T;// 返回建立的二叉查找树的根指针
}
3.插入函数
因为构造二叉树的过程中要不断插入新的结点(参考二叉树构建中的非递归法)
因此插入函数是必须要的,算法实现如下:
void InsertBST(BSTree *Tptr, KeyType key){
BSTNode *p = *Tptr, *f;// p的初值指向根结点
//总体思路:若二叉查找树*Tptr中没有关键字为key,则插入,否则直接返回
//下面开始查找插入位置:
while(p){// 若p不为空
if(key == p->key)return; // 若树中已有key,无须插入
f = p;// f保存当前查找的结点
p = (key < p->key) ? p->lchild : p->rchild;//若key < p->key,那就p = p->lchild,否则指向右孩子
}
//查找不成功,下面开始插入
//循环结束后,p为NULL,而f指向其父节点
p = (BSTNode *)malloc(sizeof(BSTNode));
//p中保留key的值,并设置p的左右孩子
p->key = key;
p->rchild = p->lchild = NULL;
if(*Tptr == NULL)//若原树为空
*Tptr = p;// 新插入的结点即为新的根
else{//插入过程,原树非空时将新结点*p作为*f的左孩子或右孩子插入
if(key < f->key){
p->lchild = f->lchild;
f->lchild = p;
}else{
p->rchild = f->rchild;
f->rchild = p;
}
}
}
4.查找函数
BSTree SearchBST(BSTree bt, KeyType key){
if(bt==NULL) return NULL; // bt为空,查找失败
else{
if(bt->key == key) return bt;
else if(bt->key > key)
return SearchBST(bt->lchild,key);
else
return SearchBST(bt->rchild, key);
}
}
5.运行:
void main(){
int i, key;
BSTree root = NULL, p;
int a[] = {55, 43, 19, 92, 6, 81, 49, 62,37, 4, 78, 83, 27,45};
printf("关键字序列为: \n");
for(i = 0; i < 14; i++){
printf("%4d", a[i]);
InsertBST(&root, a[i]);
}
printf("\n请输入你需要查询的关键字(0.Quit):");
scanf("%d", &key);
while(key){
p = SearchBST(root, key);
if(p)
printf("查询到该关键字\n");
else
printf("sorry, 没查到!\n");
printf("\n请输入你需要查询的关键字(0.Quit):");
scanf("%d", &key);
}
}
二、二叉平衡树
1.二叉平衡树的数据类型定义
typedef int ElemType;
typedef int KeyType;
typedef struct BSTNode{
ElemType data;
int bf; // 结点平衡因子
BSTNode *lchild, *rchild;//左右孩子指针
}BSTNode, *BSTree;
2.树的初始化
Status InitBSTree(BSTree *BT){ //初始化,操作结果:构造一个空的动态查找表BT
*BT = NULL;
return OK;
}
3.右旋操作函数
右旋操作示例:
算法实现如下:
Status R_Rotate(BSTree *p){ //右旋
//对以*p为根的二叉树作右旋处理,处理之后p指向新的树根结点
//即指向处理前的左子树的根节点(p的左孩子结点)
BSTree lc;
lc = (*p)->lchild; //lc指向左子树的根节点
(*p)->lchild = lc->rchild; //lc的右子树挂接为p的左子树
lc->rchild = *p;
*p = lc;// p指向新的根结点
return OK;
}
4.左旋操作函数
Status L_Rotate(BSTree *p){ //左旋
BSTree rc;
rc = (*p)->rchild;
(*p)->rchild = rc->lchild;
}
5.左平衡旋转处理函数
代码实现如下:
#define LH +1 //左高
#define EH 0 //等高
#define RH -1 //右高
Status LeftBalance(BSTree *T){//左平衡旋转处理(需要右旋或者先左后右旋)
BSTree lc, rd;//新结点在左子树上的挂法有两种:
//1.直接挂在根节点的左孩子上,造成左高
//2.挂在根节点的右孩子的左孩子上,对根结点来说造成右高,但对它的右孩子来说却是左高
//因此为了旋转,就将lc指向根节点的左孩子,rd指向根节点的左孩子的右孩子
lc = (*T)->lchild;//将lc指向根节点的左孩子
switch (lc->bf){ // 检查*T的左子树的平衡度,并作相应处理
case LH: //新结点插入在*T的左子树上,要做单右旋处理
(*T)->bf = lc->bf = EH;//因为右旋操作函数处理后的T指向新的根节点了,有变化,所以先设置好EH再去操作
R_Rotate(T);//右旋操作
break;
case RH: //新结点插入在*T的左孩子的右子树上,要做双旋处理
rd = lc->rchild;//rd指向*T的左孩子的右子树的根
switch(rd->bf){//修改*T及其左孩子的平衡因子
case LH:(*T)->bf = RH;//如果是左高,就要先左后右旋;最终结果就是T的右子树高
lc->bf = EH;
break;
case EH:(*T)->bf =lc->bf = EH;//若等高,也同样是先左后右旋
break;
case RH:(*T)->bf = EH;
lc->bf = LH;
}//switch2
rd->bf = EH;
L_Rotate(&(*T)->lchild);
R_Rotate(T);
}//switch1
return OK;
}//Leftbalance
6.右平衡处理函数
与5同理,因此直接展示代码:
Status RightBalance(BSTree *T){//有平衡旋转处理
//对以T所指向的结点为根二叉树做右平衡处理,T指向新的根结点
BSTree rc, rd;
rc = (*T)->rchild;//rc指向T的右子树根结点
switch(rc->bf){//检查*T的右子树的平衡度,并作相应平衡处理
case RH:
(*T)->bf = rc->bf = EH;
L_Rotate(T);
break;
case LH:
rd = rc->lchild;//让rc指向根节点的右孩子的左孩子
switch (rd->bf)
{//修改*T及其右孩子的平衡因子
case RH:(*T)->bf = LH;
rc->bf = EH;
case EH:(*T)->bf = rc->bf = EH;
case LH:(*T)->bf = EH;
rc->bf = RH;
}//switch
rd->bf = EH;
R_Rotate(&(*T)->rchild);
L_Rotate(T);
}//switch
return OK;
}//RightBalance
7.结点的插入函数(实质就是用来创造新结点的)
代码如下:
Status InsertElem(BSTree *T, ElemType e,Status *taller){
//若不存在e则插入,返回1;否则返回0;若插入后失衡,则作相应平衡处理。
//taller记录树高
if(!(*T)){//如果为空树,就建立根节点;
//这个语句相当于是插入语句,随着递归的深入,
//*T总会指向原二叉树的叶子结点的左/右孩子,
//此时就是在该结点下面插入新结点
*T = (BSTree)malloc(sizeof(BSTNode));
(*T)->data = e;
(*T)->lchild = (*T)->rchild = NULL;
(*T)->bf = EH;//将这个结点的平衡因子初始化为EH
*taller = TRUE;//将树高设为TRUE,相当于发送了一个信号,
//告诉上一次递归的函数:
//“新结点建立完成,可以继续执行下面的语句。”
//同时自己返回OK(1)
}else{
if(e == (*T)->data){//若存在相同结点,则不插入
taller = FALSE;
return FALSE;
}//if2
if(e < (*T)->data){//若小于,就在*T的左子树上继续寻找
if(!InsertElem(&(*T)->lchild, e, taller))//这里采用递归法在左子树上继续寻找,如果返回值为OK则成功建立,如果没建成功就返回FALSE
return FALSE;
if(*taller){ //如果树高==TRUE(也就是在插入新结点之后),把当前作为根结点开始旋转
switch ((*T)->bf)
{
case LH: //如果本来就是左高,那现在就是+2了,所以需要旋转
LeftBalance(T);
*taller = FALSE;//旋转之后变为平衡状态,所以*taller改为FALSE
break;
case EH: //如果原来是等高,现在是左高,就改变根结点的值,
//并且把*taller变成TRUE,表示不处于平衡状态
(*T)->bf = LH;
*taller = TRUE;
break;
case RH:(*T)->bf = EH;//若本来是右高,现在在左边加了一个,就平衡了
*taller = FALSE;
}//switch
}//if3
}//if1
else{//应在T的右子树中搜索
if(!InsertElem(&(*T)->rchild, e, taller))//未插入
return FALSE;
if(*taller){
switch ((*T)->bf){
case LH:
(*T)->bf = EH;
*taller = FALSE;
break;
case EH:
(*T)->bf = RH;
*taller = TRUE;
break;
case RH:
RightBalance(T);
*taller = FALSE;
}//switch
}//if
}//else
}//else
return OK;
}
8.搜索操作函数:
BSTree SearchBSTree(BSTree T, KeyType k)
if(T){//一定要考虑结点是否非空!!!如果为空就直接返回NULL
if(key == T->data)
return T;
else if(key < T->data)
return SearchBSTree(T->lchild, key);
else
return SearchBSTree(T->rchild, key);
}
return NULL;
}
9.主函数的实现方式:
void main(){
int i, taller, key, sel, n;
BSTree root, p;
ElemType a[100];
printf("请输入您自定义的关键字序列,以空格分隔,以-1表示结束\n");
n = 0;
while(scanf("%d", &a[n])&& a[n] != -1)
InsertElem(&root,a[n++],&taller);
while(sel == 2 || (sel = 'Y'||sel == 'y')){
printf("您输入的关键字序列为:\n");
for(i = 0; i < n; i++)
printf("%4d", a[i]);
printf("\n\n请输入您想要查询的关键字:");
scanf("%d", &key);
p = SearchBSTree(root, key);
if(p)
printf("查询到该关键字\n\n");
else
printf("sorry, 没查到!\n\n");
printf("继续查询码?(Y/N)");
scanf("\n%c",&sel);
}//while2