二叉排序树与平衡二叉树的实现

一、实验目的

1、问题描述

     分别采用二叉链表和顺序表作存储结构,实现对二叉排序树与平衡

 二叉树的操作。

2、设计功能要求

(1)用二叉链表作存储结构实现二叉排序树。

      1)以回车符(‘\n’)为输入结束标志,输入数列L,生成一棵二叉排序

 树T;

      2)对二叉排序树T作中序遍历,输出结果;

      3)计算二叉排序树T查找成功的平均查找长度,输出结果;

      4)输入元素x,查找二叉排序树T,若存在含x的结点,则删除该结

 点,并作中序遍历(执行操作2);否则,输出信息“无x”;

(2)用顺序表(一维数组)作存储结构—-静态链表

      1)以回车符(‘\n’)为输入结束标志,输入数列L,生成一棵二叉排序

 树T;

      2)对二叉排序树T作中序遍历,输出结果;

      3)计算二叉排序树T查找成功的平均查找长度,输出结果;

      4)输入元素x,查找二叉排序树T,若存在含x的结点,则删除该结

 点,并作中序遍历(执行操作2);否则,输出信息“无x”;

(3)用二叉链表作存储结构实现平衡的二叉排序树。

      1)用数列L,生成平衡的二叉排序树BT:当插入新元素之后,发现

 当前的二叉排序树BT不是平衡的二叉排序树,则立即将它转换成新的平

 衡的二叉排序树BT;

      2)计算平衡的二叉排序树BT的平均查找长度,输出结果。

二、算法难点

建立二叉排序树,在二叉排序树中删除结点;建立平衡二叉树并在其中插入或删除结点;计算平均查找长度。

三、解决思路及算法

1、用二叉链表作存储结构实现二叉排序树。

建二叉树的结点,包含5个成员,分别存放结点的数据data,结点在树中的高度height,左子女结点指针*lchild,子女结点指针*rchild和指向父结点的指针*parent。整个二叉树的链表要有一个表头指针*T,它指向二叉树的根结点,其作用是当作树的访问点。

InsertBST以根为起点寻找结点z该插入的位置,设当前结点为x,如果z的值小于x则将当前节点的左子结点作为下一个x,反之则以右子结点作为下一个x,如此不断向叶结点搜索,在此过程中,程序将前一个结点保存在y中,用作z的候选父结点,当x为空时y就是z的父结点。从空的二叉排序树开始,经过一系列的插入操作以后,生成了一棵二叉排序树。

中序遍历二叉树算法的框架是:若二叉树为空,则空操作;否则,中序遍历左子树(L);然后访问根结点(V);最后中序遍历右子树(R)。

在删除结点时,我们首先要查找所需要删除的节点。找到该结点p后DeleteBST需要考虑三种情况:当p为叶子结点,可直接删除;当p只有左子树或右子树时,将p的父结点直接指向左子树或右子树;当p左右子树均存在,在p的左子树上找最右结点s,若s是叶子结点,则将s的值赋给p,然后置空指针,否则,s的父结点指向s的指针将指向s的左子树,最后释放s。

计算平均查找长度时,利用中序遍历的方法,求所有结点的高度喝,再除以结点个数即可。

2、用顺序表(一维数组)作存储结构—-静态链表。

建立二叉排序树,首先用一个一维数组记录下读入的数据,数组中剩余的值用0补齐。建立二叉树,包括所有数据域*data和总结点个数dnum。每读入一个数据的同时将该数据插入到二叉树相应位置,若key小于数组中第i个数的值则将key插入到2*i的位置,反之插入到2*i+1的位置。

中序遍历二叉树也采用递归函数的方式,先访问左子树2*i,然后访问根节点i,最后访问右子树2*i+1。

计算二叉树平均查找长度时,要调用查找函数search,采用遍历数组的方式,边查找结点边累计查找长度length,然后除以总结点数即可。

删除二叉排序树结点的时候,要重新建立一个一维数组作为存储新树的空间,将原数组中的数据一个一个插入树中,若遇到需要删除的节点则不执行插入操作。

3、用二叉链表作存储结构实现平衡的二叉排序树。

首先构建平衡二叉树的结点,包括数值data,平衡因子BF,左子女结点指针*lchild,子女结点指针*rchild。

然后构建平衡旋转函数,分别有LL,LR,RL,RR四种类型,每一个旋转类型,都要对应着改变指针指向以及平衡因子。

然后是结点插入函数InsertAVL,分为三种情况:原本左子树比右子树高,需要做左平衡处理;原本左右子树等高,现因左子树增高而树增高;原本右子树比左子树高,现在左右子树等高。

中序遍历算法和计算平均查找长度算法同上。

四、测试样例

1、用二叉链表作存储结构实现二叉排序树。

《二叉排序树与平衡二叉树的实现》

《二叉排序树与平衡二叉树的实现》

 

2、用顺序表(一维数组)作存储结构—-静态链表

 《二叉排序树与平衡二叉树的实现》

《二叉排序树与平衡二叉树的实现》

3、用二叉链表作存储结构实平衡的二叉排序树。

《二叉排序树与平衡二叉树的实现》

《二叉排序树与平衡二叉树的实现》

五、反思启发
在编写代码的过程中,我经常分不清楚BSTNode 和BSTree以及是否要带*,导致及时明白算法过程,但编写过程出现了巨大的问题。在建立树的函数就有很多的表达方式,我需要考虑这个函数对后面函数的影响。同时,在编写删除结点的函数和计算平均查找长度的函数也花费了不少的时间,在这一过程中,我对链表和指针的应用能力得到了极大的提升。
六、代码附录
1、用二叉链表作存储结构实现二叉排序树。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct Node
{
int data;
int height;
Node *rchild,*lchild,*parent;
}BSTNode,*BSTree;
BSTNode *T;
void insertBST(int k)
{
BSTNode *y=NULL;
BSTNode *x=T;
BSTNode *z;
z=(BSTNode *)malloc(sizeof(Node));
z->data=k;
z->height=1;
z->lchild=NULL;
z->rchild=NULL;
while(x!=NULL)
{
y=x;
if(z->data<x->data)
{
x=x->lchild;
z->height++;
}
else
{
x=x->rchild;
z->height++;
}
}
z->parent=y;
if(y==NULL) T=z;
else
{
if(z->data<y->data) y->lchild=z;
else y->rchild=z;
}
}
void Inorder(BSTNode *u)
{
if(u==NULL) return;
Inorder(u->lchild);
printf(“%d “,u->data);
Inorder(u->rchild);
}
double CalLength(BSTree *T)
{
static double length=0;
static int n=0;
if(*T)
{
length+=(*T)->height;
CalLength(&(*T)->lchild);
n++;
CalLength(&(*T)->rchild);
}
return length/n;
}
BSTree SearchDeleteBST(int  key)  //删除函数
{
BSTree p=T,q=NULL,s,f;
   while(p!=NULL)   //查找要删除的点
    {
        if(p->data==key)
break;
        q=p; //q指向要删结点的父母
        if(p->data>key)   p=p->lchild;
        else p=p->rchild;
    }
    if(p==NULL)
return  T;    //查找失败
    if(p->lchild==NULL)     //p指向当前要删除的结点
    {
        if(q==NULL)   T=p->rchild;  
        else if(q->lchild==p)    q->lchild=p->rchild;  //p为q的左孩子
        else q->rchild=p->rchild; //p为q的右孩子
        free(p);
    }
else
{           //p的左孩子不为空
        f=p;
        s=p->lchild;
        while(s->rchild)   //左拐后向右走到底
        {
            f=s;
            s=s->rchild;
        }
        if(f==p)    f->lchild=s->lchild;  //重接f的左子树
        else    f->rchild=s->lchild;   //重接f的右子树
        p->data=s->data;
        free (s);
    }
    return  T;
}
int searchBST(BSTree T,int key,BSTree f,BSTree *p) //查找函数
{
    if(!T)
{
*p=f;
return 0;
}         //查找不成功
else if(key==T->data)    
{
*p=T;
return 1;
} /*查找成功*/
else if(key<T->data)   searchBST(T->lchild,key,T,p);   //在左子树中继续查找
else  searchBST(T->rchild,key,T,p); //在右子树中继续查找
}
int main()
{
    int num;
    char ch;
    BSTree p=NULL;
printf(“输入一串数,每个数以空格分开:”);
    do
{
        scanf(“%d”,&num);
        insertBST(num);
        scanf(“%c”,&ch);
        if(ch==’\n’) break;
    }while(ch!=’\n’);
    printf(“中序遍历输出结果为:\n”);
    Inorder(T); 
    printf(“\n”);
    printf(“平均查找长度为:%3.1f”,CalLength(&T));
    printf(“\n”);
    printf(“输入查找元素并删除该结点:”);
    scanf(“%d”,&num); 
    if(searchBST(T,num,NULL,&p))
    {
        T=SearchDeleteBST(num);
        printf(“\n删除成功!中序遍历输出:\n”);
        Inorder(T);
    }
    else
        printf(“无%d”,num);
    return 0;
}
2、用顺序表(一维数组)作存储结构—-静态链表
#include<stdio.h>
#include<stdlib.h>
#define N 100
typedef struct
{
int *data;
int dnum;
}BST;
float length;
void InsertBST(BST T,int i,int key)
{
if(i<1||i>N) 
printf(“ERROR!\n”);
if(T.data[i]==0) 
T.data[i]=key;
else if(key<T.data[i]) 
InsertBST(T,2*i,key);
else InsertBST(T,2*i+1,key);
}
BST CreatBST(int *A,int num)
{
BST T;
int i,j;
T.data=(int *)malloc(N*sizeof(int));
for(j=0;j<N;j++)
T.data[j]=0;
T.dnum=0;
for(i=0;i<num;i++)
{
InsertBST(T,1,A[i]);
++T.dnum;
}
return T;
}
void Inorder(BST T,int i)
{
if(T.data[i])
{
Inorder(T,2*i);
printf(“%d “,T.data[i]);
Inorder(T,2*i+1);
}
}
int search(BST T,int key,int i)
{
length++;
if(!T.data[i]) return 0;
else if(key==T.data[i]) return i;
else if(key<T.data[i]) search(T,key,2*i);
else search(T,key,2*i+1);
}
BST DeleteBST(BST T,int key)
{
BST q;
int i;
q.data=(int *)malloc(N*sizeof(int));
for(i=0;i<N;i++) 
q.data[i]=0;
q.dnum=0;
for(i=1;i<N&&T.dnum>0;i++)
{
if(T.data[i]==0||T.data[i]==key) continue;   
InsertBST(q,1,T.data[i]);   
–T.dnum;   
++q.dnum;
}
delete T.data;
return q;
}
int main()
{
BST T;
int A[N];
char ch;
int i=0;
int num,j;
printf(“请输入数据:”);
do
{
scanf(“%d”,&num);
A[i++]=num;
        scanf(“%c”,&ch);
        if(ch==’\n’) break;
}while(ch!=’\n’);
T=CreatBST(A,i);
printf(“中序遍历结果为:”);
Inorder(T,1);
printf(“\n”);
length=0;
for(int s=0;s<T.dnum;s++)
search(T,A[s],1);
printf(“平均查找长度为:”);
float f;
f=length/T.dnum;
printf(“%3.1f\n”,f);
printf(“输入查找元素并删除该结点:”);
scanf(“%d”,&num);
j=search(T,num,1);
if(j)
{
T=DeleteBST(T,num);
printf(“中序遍历结果为:”);
Inorder(T,1);
i–;

else printf(“无%d”,num);
}
3、用二叉链表作存储结构实平衡的二叉排序树。
#include<stdio.h>
#include<stdlib.h>
#define LH 1
#define EH 0
#define RH -1
typedef struct Node
{
int data;
int BF;//平衡因子(balance factor)
struct Node *lchild,*rchild;
}BSTNode,*BSTree;
void R_Rotate(BSTree *p)//以p为根节点的二叉排序树进行右旋转
{
BSTree L;
L=(*p)->lchild;
(*p)->lchild=L->rchild;
L->rchild=(*p);
*p=L;//p指向新的根节点
}
void L_Rotate(BSTree *p)//以p为根节点的二叉排序树进行左旋转
{
BSTree R;
R=(*p)->rchild;
(*p)->rchild=R->lchild;
R->lchild=(*p);
*p=R;
}
void LeftBalance(BSTree *T)
{
BSTree L,Lr;
L=(*T)->lchild;
switch(L->BF)
{
  //检查T的左子树平衡度,并作相应的平衡处理
case LH://新节点插入在T的左孩子的左子树上,做单右旋处理
(*T)->BF=L->BF=EH;
R_Rotate(T);
break;
case RH://新插入节点在T的左孩子的右子树上,做双旋处理
Lr=L->rchild;
switch(Lr->BF)
    {
    case LH:
    (*T)->BF=RH;
    L->BF=EH;
    break;
    case EH:
    (*T)->BF=L->BF=EH;
    break;
    case RH:
    (*T)->BF=EH;
    L->BF=LH;
    break;
    }
Lr->BF=EH;
    L_Rotate(&(*T)->lchild);
    R_Rotate(T);
}
}
void RightBalance(BSTree *T)

BSTree R,Rl;
R=(*T)->rchild;
switch(R->BF)
{
case RH://新节点插在T的右孩子的右子树上,要做单左旋处理
        (*T)->BF=R->BF=EH;
    L_Rotate(T);
    break;
case LH://新节点插在T的右孩子的左子树上,要做双旋处理
    Rl=R->lchild;
    switch(Rl->BF)
    {
    case LH:
    (*T)->BF=EH;
    R->BF=RH;
    break;
    case EH:
    (*T)->BF=R->BF=EH;
    break;
    case RH:
    (*T)->BF=LH;
    R->BF=EH;
    break;
    }
    Rl->BF=EH;
  R_Rotate(&(*T)->rchild);
  L_Rotate(T);
 }
}
bool InsertAVL(BSTree *T,int e,bool *taller)//变量taller反应T长高与否
{
if(!*T)
{
*T=(BSTree)malloc(sizeof(BSTNode));
(*T)->data=e;
(*T)->lchild=(*T)->rchild=NULL;
(*T)->BF=EH;
*taller=true;
}
else
{
if(e==(*T)->data)//不插入
{
*taller=false;
return false; 
}
if(e<(*T)->data)
{
if(!InsertAVL(&(*T)->lchild,e,taller))//未插入
  return false;
if(*taller)//以插入左子树,且左子树变高
{
switch((*T)->BF)
{
    case LH://原本左子树比右子树高,需要做左平衡处理
    LeftBalance(T);
    *taller=false;
    break;
    case EH://原本左右子树等高,现因左子树增高而树增高
    (*T)->BF=LH;
    *taller=true;
    break;
    case RH://原本右子树比左子树高,现在左右子树等高
    (*T)->BF=EH;
    *taller=false;
    break;
    }
}
}
else
{
//应在T的右子树中搜寻
if(!InsertAVL(&(*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;
      break;
}
    }
  }
}
 return true;
}
bool Find(BSTree T,int key)
{
if(!T)
return false;
else if(T->data==key)
return true;
else if(T->data<key)
return Find(T->rchild,key);
else
return Find(T->lchild,key);
}
void Inorder(BSTree T)
{
if(T)
{
printf(“%d”,T->data);
if(T->lchild||T->rchild)
{
printf(“(“);
Inorder(T->lchild);
printf(“,”);
Inorder(T->rchild);
printf(“)”);
}
}
}
double CalAveLength(BSTree T,int A[],int num)
{
double len=0;
int i;
BSTree rt;

for(i=0;i<num;i++)
{
rt=T;
for(;;)
{
len++;
if (rt->data==A[i]) break;
if(rt->data<A[i])
rt=rt->rchild;
else
rt=rt->lchild;
}
}
return (double)len/num;
}
int main()
{
int i;
int j;//插入数 
int num;
int A[]={3,2,1,4,5,6,7,10,9,8};
BSTree T=NULL;
bool taller;
num=sizeof(A)/sizeof(int);
for(i=0;i<sizeof(A)/sizeof(int);i++)
InsertAVL(&T,A[i],&taller);
Inorder(T);
printf(“\n”);
printf(“插入数:”);
scanf(“%d”,&j);
InsertAVL(&T,j,&taller);
Inorder(T);
printf(“\n”);
printf(“二叉排序树查找成功的平均查找长度为:%3.1f”,CalAveLength(T,A,num));
return 0;

}

    原文作者:平衡二叉树
    原文地址: https://blog.csdn.net/qq_39717454/article/details/80902574
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞