平衡二叉树

2021年5月3日 59点热度 0条评论 来源: 洗菜剑心

博客思路

1.通过二叉排序树引出平衡二叉树

2.如何判断是不是一棵平衡二叉树

3.平衡因子

4.左旋  右旋   双旋

5.通过画图创建一棵二叉排序树

6.二叉排序树的代码思路和整体框架

 

1.二叉排序树

二叉排序树,又叫二叉查找树,它或者是一棵空树;或者是具有以下性质的二叉树:
1. 若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
2. 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
3. 它的所有结点的左右子树也分别为二叉排序树。

讲平衡二叉树之前我们讲一下二叉排序树。

看看下面两张图

这两个都是二叉排序树吗?没错你看下上面的概念的话,发现两个都是二叉排序树。只是右边的树长的丑了一点,它是一棵右斜树。它们都是一样的关键字1,2,3,4,5,6,7,8,9 只是根结点取得不一样,排出来就长的不一样。

再来看看这两棵树的查找效率。我的天!这个斜树查找效率和链表的循序遍历效率都一模一样了。可见二叉排序树的查找效率是有点看运气的。想要建立一棵好看的二叉排序树,这个和快排有点像,知道一个数组所有的数字,每次取基准点,这里是取根,取越中间越好。如果每棵子二叉排序树都能取到尽量靠中间的数值当根结点时候,排出来的树越好看(也就是高度差低)。但是如果后面要继续对树进行插入删除,又改变了树的结构,是不是这棵树的长相就变得不可控了。查找效率也变得不可控。于是引出今天的平衡二叉树。

2.平衡二叉树

(AVL)平衡二叉树是一种二叉排序树,其中每个结点的左子树和右子树的高度差至多等于1。它是一种高度平衡的二叉排序树。意思是说,要么它是一棵空树,要么它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。

平衡二叉树是一种二叉排序树。也就是二叉排序树包含了平衡二叉排序树,其次它要满足后面的约束条件,就是每个结点的左子树和右子树的高度差不超过1

我先看几张图片判断一下哪些是平衡二叉树。

 

看完上面的图相信大家应该能判断出来什么树是平衡二叉树了

 

平衡因子

平衡因子就是在平衡二叉树结点的结构体引进的一个int 变量,用来记录该结点的(左子树整体高度 - 右子树整体高度)。平衡二叉树的平衡因子只能为(-1,0,1)。

画个图解释下

 

个人心得:下面到了重点部分了,其实我在学平衡二叉树也是从这里开始有些困惑,但是这时候先不要去考虑所有的问题,先去考虑下局部问题,那就是什么是左旋  右旋 双旋。  因为我们知道建立一棵平衡二叉树,关键是要降低整个树的高度差。所以不要被一些(左旋    右旋   双旋)名词吓到了。就是个调高度差的东西罢了。弄清楚了左旋 右旋  双旋

平衡二叉树的旋转问题

要学习平衡二叉树之前我们要先看一下左旋  右旋  双旋,我们知道当我们插入一个结点在平衡二叉树上时候,整个平衡二叉树可能左右子树的高度差可能发生了变化,而左旋  右旋  双旋目的就是为了降低整个树的高度差,使得它们左右子树的高度差不超过1。

左旋

先看一下图

 

下面是左旋转时候,新根带有左子树的

 

我大致要描述的文字在图里已经描述了我就不再描述了

看一下左旋代码

typedef struct BiNode
{
	int data;        //这里可以对data 类型进行宏定义,这里是方便理解
	int bf;           //平衡因子
	struct BiNode *lchild,*rchild;
}BiNode,*BiTree;



void L_Rotate(BiTree *p)
{
    BiTree L;                       //(*p) p的解引用还是一个指针,指向上图2结点
    L = (*P)->rchild;             //指针L指向上图的4结点   
    (*p)->rchild = L->lchild;      //让(*p) 也就是2结点右子树 = 4结点的左子树
    L->lchild = (*p);              //   让4结点的左子树  = 2结点;
    *p = L;                       //把4结点转化成新根
      
}

看完左旋我就不讲右旋了,真的是一样理解的。或者最后看总代码时候再理解把

 

双旋是什么鬼我们先把左旋和右旋解决不了的问题找出来先

这个便需要我们双旋,不要想那么复杂 。看完图片相信大家能找到问题所在了。现在就是要解决这个问题,我们要先找到

那个符号不统一的9结点,然后对它先进行右旋,在进行左旋。这就是双旋

这就是所谓的双旋。这里的符号统一我可能没描述清楚,是失衡结点,和要当新根结点符号统一就好了。我再画一张图。

 

说下当右子树失衡之后的调整的代码思路

当我们发现一棵树右边因为插入结点失衡了,要进行调整了

传进 T  是一个二级指针,指向一个失衡结点,这个失衡结点也就是从下往上看第一个平衡因子绝对值超过1的结点。因为是右子树平衡调整。我就知道要调整是这个失衡点的右子树。

1)定义 p = (*T)->rchild;  让它指向失衡结点的右子树。

2)利用switch p 的平衡因子的大小做出是左旋  还是双旋的操作

3)发现是p 的右子树高,证明可以直接左旋,左旋前要调整平衡因子大小。

调整平衡因子的逻辑我画个图这是推导(*T),p也可以推导

4)发现p的平衡因子 为LH     平衡因子符号不统一

这时候我们需要做的本身是先右旋再左旋但是我们要先调节它的平衡因子

5)于是多了一个switch(pl->bf)调整它的平衡因子的。这里我就不画图讲解了

6)右旋再左旋

#define LH   1
#define EH   0
#define RH  -1


void RightBalance(BiTree *T)
{
    BiTree p,pl;
    p = (*T)->rchild;        //已经知道是右边高了才失衡,所以p指针直接指向它的右子树    
    
    switch (p->bf)         //这个是看要左旋还是  双旋,再调它的平衡因子            
    {
           case RH:             //p右子树高,证明平衡因子符号统一,直接左旋      
            (*T)->bf = p->bf = EH;     //看上图
            L_Rotate(T);            //左旋函数
            break;
            case LH:             //p左子树高,要双旋先调平衡因子  
             pl = p->lchild;
            switch(pl->bf)
            {
                case RH:
                (*T)->bf = LH;
                p->bf = EH;
                break;
                case EH:
                 (*T)->bf = p->bf = EH; 
                case LH:
                (*T)->bf = EH;
                p->bf = RH;
                break;
            }
            pl->bf = EH;              
            R_Rotate(&(*T)->rchild);    //把它的右子树先右旋,平衡因子符号统一
            L_Rotate(T);              //左旋
    }
}

左平衡我就不说了。一样的推导

 

看完前面再看现在的代码就轻松很多了自己看一下吧。

#include <iostream>
using namespace std;

#define  LH  1
#define  EH  0
#define  RH  -1
#define  ElemType  int
#define TRUE   1
#define FALSE   0

typedef struct BiNode
{
    ElemType data;
    int bf;
    struct BiNode  *lchild;
    struct  BiNode *rchild;
}BiNode,*BiTree;


void L_Rotate(BiTree *p)      //左旋
{
    BiTree R;                       
    R = (*p)->rchild;               
    (*p)->rchild = R->lchild;      
    R->lchild = (*p);              
    *p = R;                         
}


void R_Rotate(BiTree *p)    //右旋
{
    BiTree L;
    L = (*p)->lchild;
    (*p)->lchild = L->rchild;
    L->rchild = (*p);
    *p = L;
}


void RightBalance(BiTree *T)    //右平衡调整
{
    BiTree R,Rl;
    R = (*T)->rchild;        //已经知道是右边高了才失衡,所以R指针直接指向它的右子树    
    
    switch (R->bf)         //这个是看要左旋还是  双旋,再调它的平衡因子            
    {
           case RH:             //R右子树高,证明平衡因子符号统一,直接左旋      
            (*T)->bf = R->bf = EH;    
            L_Rotate(T);            //左旋函数
            break;
            case LH:             //R左子树高,要双旋先调平衡因子  
             Rl = R->lchild;
            switch(Rl->bf)    //调整平衡因子大小
            {
                case RH:
                (*T)->bf = LH;
                R->bf = EH;
                break;
                case EH:
                 (*T)->bf = R->bf = EH; 
                case LH:
                (*T)->bf = EH;
                R->bf = RH;
                break;
            }
            Rl->bf = EH;              
            R_Rotate(&(*T)->rchild);    //把它的右子树先右旋,平衡因子符号统一
            L_Rotate(T);              //左旋
    }
}


void LeftBalance(BiTree *T)
{
    BiTree L ,Lr;
    L = (*T)->lchild;

    switch(L->bf)
    {
        case LH:
        (*T)->bf = L->bf = EH;
        R_Rotate(T);
        break;
    
        case RH:
        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);
    }
}


int InsertAVL(BiTree *T,ElemType val,int *taller)
{
    if(!*T)
    {
        *T = (BiTree)malloc(sizeof(BiNode));
        (*T)->data = val;
        *taller = TRUE;
    }
    else
    {
        if(val == (*T)->data)
        {
            *taller = FALSE;
            return FALSE;
        }
          
        if(val < (*T)->data)
        {
                if(!InsertAVL(&(*T)->lchild,val,taller))
                {
                    *taller = FALSE;
                    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;  
                            break;
                             }
      
               }
        }
        else
        {
                if(!InsertAVL(&(*T)->rchild,val,taller))
                {
                    return FALSE;    
                }

                if(*taller)
                {
                    switch((*T)->bf)
                    {
                             case LH:
                                   (*T)->bf = EH;
                                   *taller = FALSE;
                                    break;
                              case EH:
                                   (*T)->bf = EH;
                                    *taller = TRUE;
                                    break;
                             case RH:
                                   RightBalance(T);
                                    *taller = FALSE;
                                       break;  

                    }        

                }
                               
        }          
    }
    return TRUE;
}





我说一下带头节点的代码思路

1)在Insert(AVLTree *ptree,KeyType kx)

先看判断有没有头节点

有头节点在看有没有根,没根就把kx当根

while循环目的看是否存在kx,如果没有,就找到kx要插入的父节点,而且一定是插在叶子结点的

2)Insert_Item(AVLTree *ptree,AVLNode *pa,KeyType kx)

先创建一个结点,然后对其初始化,把data = kx

判断是kx所在的结点是pa的左孩子还是右孩子,两者建立好连接

然后对他们的平衡因子进行调整。引入tall。这部分得自己理解,不太好讲。自己领悟下就出来了。

#include<iostream>
#include<stack>
#include<queue>
using namespace std;

#define LH   1
#define EH   0
#define RH  -1


typedef int KeyType;
typedef struct AVLNode
{
	AVLNode *leftchild;
	AVLNode *parent;
	AVLNode *rightchild;
	int balance; // 0 1 -1;
	KeyType key;
}AVLNode;
typedef struct
{
	AVLNode *head;
	int cursize;
}AVLTree;


AVLNode * Buynode()
{
	AVLNode *s = (AVLNode*)malloc(sizeof(AVLNode));
	if(NULL == s) exit(1);
	memset(s,0,sizeof(AVLNode));
	return s;
}

void Freenode(AVLNode *p)
{
	free(p);
}

void Init_AVLTree(AVLTree *ptree)
{
	if(ptree == NULL) exit(1);
	ptree->head = Buynode();
	ptree->cursize = 0;
}

AVLNode * FindValue(AVLTree *ptree,KeyType kx)
{
	if(ptree == NULL || ptree->head == NULL)
		return NULL;
	AVLNode *p = ptree->head->parent;
	while(p != NULL && p->key != kx)
	{
		p = kx < p->key? p->leftchild:p->rightchild;
	}
	return p;
}

AVLNode *Search(AVLNode *ptr,KeyType kx)
{
	if(ptr == NULL || ptr->key == kx) 
		return ptr;
	else if(kx <ptr->key)
	{
		return Search(ptr->leftchild,kx);
	}else
	{
		return Search(ptr->rightchild,kx);
	}
}

AVLNode * SearchValue(AVLTree *ptree, KeyType kx)
{
	if(ptree == NULL || ptree->head == NULL)
		return NULL;
	else
		return Search(ptree->head->parent,kx);
}

void RotateRight(AVLTree *ptree,AVLNode *ptr)
{
	AVLNode *newroot = ptr->leftchild;
	newroot->parent = ptr->parent;
	ptr->leftchild = newroot->rightchild;
	if(newroot->rightchild != NULL)
	{
		newroot->rightchild->parent = ptr;
	}
	newroot->rightchild = ptr;
	if(ptree->head->parent == ptr) //root
	{
		ptree->head->parent = newroot;
	}
	else
	{
		if(ptr->parent->leftchild == ptr)
		{
			ptr->parent->leftchild = newroot;
		}
		else
		{
			ptr->parent->rightchild = newroot;
		}
	}
	ptr->parent = newroot;
}

void RotateLeft(AVLTree *ptree, AVLNode *ptr)
{
	AVLNode *newroot = ptr->rightchild;
	newroot->parent = ptr->parent; // 1
	ptr->rightchild = newroot->leftchild;
	if(newroot->leftchild != NULL)
	{
		newroot->leftchild->parent = ptr; //2
	}
	newroot->leftchild = ptr;
	if(ptr == ptree->head->parent) // root
	{
		ptree->head->parent = newroot;
	}
	else
	{
		if(ptr->parent->rightchild == ptr)
		{
			ptr->parent->rightchild = newroot;
		}
		else
		{
			ptr->parent->leftchild = newroot;
		}
	}
	ptr->parent = newroot;//3
}

void RightBalance(AVLTree *ptree,AVLNode *ptr)
{
	AVLNode *rightsub = ptr->rightchild, *leftsub= NULL;
	switch(rightsub->balance)
	{
	case EH: cout<<"right already balance"<<endl; break;
	case RH: 
		ptr->balance = EH;
		rightsub->balance = EH;
		RotateLeft(ptree,ptr);
		break;
	case LH:
		leftsub = rightsub->leftchild;
		switch(leftsub->balance)
		{
		case EH: 
			ptr->balance = EH;
			rightsub->balance = EH;
			break;
		case RH:
			ptr->balance = LH;
			rightsub->balance = EH;
			break;
		case LH:
			ptr->balance = EH;
			rightsub->balance = RH;
			break;
		}
		leftsub->balance = EH;
		RotateRight(ptree,rightsub);
		RotateLeft(ptree,ptr);
		break;
	}
}

void LeftBalance(AVLTree *ptree,AVLNode *ptr)               //整个树的左边失衡了要用这个函数进行调整
{
	AVLNode *leftsub = ptr->leftchild, *rightsub = NULL;      //ptr  是要作为新根的结点父结点
	switch(leftsub->balance)
	{
	case EH: cout<<"left already balance"<<endl; break;
	case LH:
		ptr->balance = EH;
		leftsub->balance = EH;
		RotateRight(ptree,ptr);
		break;
	case RH:
		rightsub = leftsub->rightchild;
		switch(rightsub->balance)     //这里看的是ptr ->rightchild  结点的平衡因子
		{
		case EH:                        //自己推导平衡因子的变化,这画图
			ptr->balance = EH;
			leftsub->balance = EH;
			break;
		case RH:                            
			ptr->balance = EH;
			leftsub->balance = LH;
			break;
		case LH:
			ptr->balance = RH;
			leftsub->balance = EH;
			break;
		}
		rightsub->balance = EH;
		RotateLeft(ptree,leftsub);
		RotateRight(ptree,ptr);
		break;
	}

}

void Insert_Item(AVLTree *ptree,AVLNode *pa,KeyType kx)
{
	AVLNode *s = Buynode();
	s->key = kx;
	s->parent = pa;
	s->balance = EH;       //新插入的结点是叶子结点,必是平衡
	if(s->key < pa->key)     //先判断是插在pa的左还是右,这里是插在pa左孩子结点上
	{
		pa->leftchild = s;          //pa左孩子 = 新插入的s结点
		if(s->key < ptree->head->leftchild->key)            //头的左孩子结点保存的是整个树的最小结点  头结点 != 根结点
		{
			ptree->head->leftchild = s;
		}
	}
	else
	{
		pa->rightchild = s;                               
		if(s->key > ptree->head->rightchild->key)
		{
			ptree->head->rightchild = s;
		}
	}
	
	AVLNode *_X = s;
	AVLNode *_Y = s->parent;
	int tall = 1;
	for(;tall == 1 && _Y != ptree->head; )
	{
		if(_Y->rightchild == _X)                   //依旧判断是左孩子还是右孩子
		{
			switch(_Y->balance)                  
			{
			case EH: _Y->balance = RH; break;          
			case LH: _Y->balance = EH; 
				tall = 0;
				break;
			case RH:
				RightBalance(ptree,_Y);
				tall = 0;
				break;
			}
		}
		else // _Y->leftchild == _X
		{
			switch(_Y->balance)
			{
			case EH: _Y->balance = LH; break;
			case RH: _Y->balance = EH; 
				tall = 0;
				break;
			case LH:
				LeftBalance(ptree,_Y);
				tall = 0;
				break;
			}
		}

		_X = _Y;
		_Y = _Y->parent;
	}	
}

bool Insert(AVLTree *ptree,KeyType kx)
{
	if(ptree == NULL || ptree->head == NULL)
		return false;
	if(ptree->head->parent == NULL)    //空树
	{
		AVLNode *s = Buynode();
		s->key = kx;
		s->parent = ptree->head;
		ptree->head->parent = s;
		ptree->head->leftchild = s;
		ptree->head->rightchild = s;
		ptree->cursize = 1;
		return true;
	}
	///
	AVLNode *pa = ptree->head; // 
	AVLNode *p = ptree->head->parent; // root;
	while(p != NULL && p->key != kx)    //判断是否存在该关键字kx  和找到要插入的位置
	{
		pa = p;
		p = kx < p->key? p->leftchild:p->rightchild;
	}
	if(p != NULL && p->key == kx) return false;   //找到关键字kx  推出
	
	Insert_Item(ptree,pa,kx);    //没找到插入kx , 此时pa 便是要插入的父结点
	ptree->cursize+=1;           //有效结点加1
	return true;
}


void showInorder(AVLNode *p)
{
	if(p != NULL)
	{
		showInorder(p->leftchild);
		cout<<p->key<<" ";
		cout<<p->balance<<" ";
		showInorder(p->rightchild);
	}
}

 

 

 

    原文作者:洗菜剑心
    原文地址: https://blog.csdn.net/xiaohui987987/article/details/81347463
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系管理员进行删除。