数据结构之二叉树的应用——二叉平衡查找树

二叉查找树和二叉平衡查找树

一.二叉查找树

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
     
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值