数据结构与算法分析之平衡二叉树的建立

  • 平衡二叉树的概念:
    平衡二叉树是二叉查找树的一个进化体,也是第一个引入平衡概念的二叉树。平衡二叉树又称AVL树。

  • 平衡二叉树的性质:
    对于每个结点来说,它的左右子树的高度之差不能超过1,如果插入或者删除一个结点是的高度之差大于1,就要进行结点之间的旋转,将二叉树重新维持在一个平衡状态。

  • 下面会一步一步的讲解如何写平衡二叉树,重点是平衡二叉树的核心部分,也就是旋转算法。

    -第一步:创建节点信息
    相对于二叉排序树的节点来说,我们需要用一个参数描述二叉树的高度,目的是维护插入和删除过程中的旋转算法。

//AVL树节点信息
//类模板,类中的内置类型都可以用模板形参名T声明
template<class T>
class TreeNode
{
public:
//类的声明,初始化列表       
TreeNode():lson(NULL),rson(NULL),freq(1),hgt(0){}
        T data;//值
        int hgt;//以此节点为根的树的高度
        unsigned int freq;//频率
        TreeNode* lson;//指向左儿子的地址
        TreeNode* rson;//指向右儿子的地址
};
  • 第二步:平衡二叉树类的声明

声明中的旋转函数将在后边的步骤中详解。

//AVL树类的属性和方法声明
template<class T>
class AVLTree
{
    private:
        TreeNode<T>* root;//根节点
        void insertpri(TreeNode<T>* &node,T x);//节点插入
        TreeNode<T>* findpri(TreeNode<T>* node,T x);//节点查找
        void insubtree(TreeNode<T>* node);//中序遍历
        void Deletepri(TreeNode<T>* &node,T x);//删除节点
        int height(TreeNode<T>* node);//求树的高度
        void SingRotateLeft(TreeNode<T>* &k2);//LL左左情况下的旋转
        void SingRotateRight(TreeNode<T>* &k2);//RR右右情况下的旋转
        void DoubleRotateLR(TreeNode<T>* &k3);//LR左右情况下的旋转
        void DoubleRotateRL(TreeNode<T>* &k3);//RL右左情况下的旋转
        int Max(int cmpa,int cmpb);//求最大值

    public:
        AVLTree():root(NULL){}
        void insert(T x);//插入接口
        TreeNode<T>* find(T x);//查找接口
        void Delete(T x);//删除接口
        void traversal();//遍历接口

};
  • 第三步:两个辅助方法
    旋转算法需要借助两个辅助的功能:
    一个是求树的高度,一个是求两个高度的最大值。这里规定,一棵空树的高度为-1,只有一个根节点的数的高度为0,以后每多一层高度增加1。
//计算以节点为根的树的高度
template<class T>
int AVLTree<T>::height(TreeNode<T>* node)
{
    if(node!=NULL)
    //传入了高度的数据
        return node->hgt;
    return -1;
}
//求最大值
template<class T>
int AVLTree<T>::Max(int cmpa,int cmpb)
{
    //判断大小的运算
    return cmpa>cmpb?cmpa:cmpb;
}

第四步:旋转函数
对于一个平衡的节点,由于任意节点最多有两个儿子,因此高度平衡时,此节点的两颗子树的高度差2,一般为下列四种情况:这里写图片描述
 1、6节点的左子树3节点高度比右子树7节点大2,左子树3节点的左子树1节点高度大于右子树4节点,这种情况成为左左。
 2、6节点的左子树2节点高度比右子树7节点大2,左子树2节点的左子树1节点高度小于右子树4节点,这种情况成为左右。
 3、2节点的左子树1节点高度比右子树5节点小2,右子树5节点的左子树3节点高度大于右子树6节点,这种情况成为右左。
 4、2节点的左子树1节点高度比右子树4节点小2,右子树4节点的左子树3节点高度小于右子树6节点,这种情况成为右右。
 
分析:从上图可以看出,1和4两种情况是对称的,简单的说,LL和RR情况是只要经过一次旋转就可以达到平衡,我们称之为单旋转。2和3两种情况也是对称的,RL和LR。这两种情况的旋转算法也是一致的,需要进行两次旋转,我们称为双旋转
2. 第五步:单旋转
单旋转是针对于LL和RR两种情况的解决方案,下图是LL情况的解决方案,结点k2不满足平衡特性,因为它的左子树k1比右子树Z高2层,而且在k1子树中,更深一层的是k1的左子树X子树,所以是LL情况。这里写图片描述
为了恢复树的平衡,我们需要把K1变成这棵树的根节点,因此把K2结点顺时针旋转,然后把K1的右子树设置成K2的左子树,这样就满足了平衡二叉树的性质了。

//左左情况下的旋转
template<class T>
void AVLTree<T>::SingRotateLeft(TreeNode<T>* &k2)
{
    //分为三步:K2的左子树连接上K1,K1的右子树连接上K2的左子树,最后K1变成根结点,右子树连接上K2。
    TreeNode<T>* k1;
    k1=k2->lson;
    k2->lson=k1->rson;
    k1->rson=k2;
//计算K2结点的高度(取左子树和右子树最高的一个)
    k2->hgt=Max(height(k2->lson),height(k2->rson))+1;
//计算K1结点的高度,同理。
    k1->hgt=Max(height(k1->lson),k2->hgt)+1;
}
//右右情况下的旋转
template<class T>
void AVLTree<T>::SingRotateRight(TreeNode<T>* &k2)
{
//与上述LL型的唯一区别就是第二步,把K1的左子树连接上K2的右子树上。
    TreeNode<T>* k1;
    k1=k2->rson;
    k2->rson=k1->lson;
    k1->lson=k2;

    k2->hgt=Max(height(k2->lson),height(k2->rson))+1;
    k1->hgt=Max(height(k1->rson),k2->hgt)+1;
}

第六步:双旋转
对于LR和RL这两种情况,是需要经过两次旋转。双旋转是针对这两种情况的解决方案,下面以LR情况为例。
这里写图片描述
为了使树恢复平衡,我们需要经过两步旋转,第一步,把K1作为根,进行一次RR旋转,旋转之后变成LL情况,所以第二步再进行一次LL旋转,最后得到平衡二叉树。

template<class T>
void AVLTree<T>::DoubleRotateLR(TreeNode<T>* &k3)
{
//LR型,第一步进行RR旋转,以K3的子叶为根进行;第二步进行LL旋转,以K3为根进行。
    SingRotateRight(k3->lson);
    SingRotateLeft(k3);
}
//右左情况的旋转
template<class T>
void AVLTree<T>::DoubleRotateRL(TreeNode<T>* &k3)
{
//RL型,第一步进行LL旋转,以K3的子叶为根旋转;第二步进行RR旋转,以K3为根进行。
    SingRotateLeft(k3->rson);
    SingRotateRight(k3);
}

转载:http://www.cppblog.com/cxiaojia/archive/2012/08/20/187776.html
特别鸣谢~

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值