平衡二叉树--AVL 的原理及实现

 

 

平衡二叉树

  1. 前言:

        对于平衡二叉树,其实是二叉搜索树的进阶版;

  • 即平衡二叉树一定是二叉搜索树
  • 每个节点的左子树和右子树的高度差的绝对值至多为1

 

       对于普通的二叉搜索树(7,3,10,12,5,1,9),其树为:

                                                               

但是如果我们的数据是这样的(1,2,3,4,5,6),那树的样子为:

                                                            

如果是这样,我们查找数字6的效率就大大降低,那有什么方法能解决呢?

那就是平衡二叉树:

2.概述:

  •  在二叉搜索树的特点的基础上,平衡二叉树还要求对于任意节点X,X的左、右子树的深度相差最大不能超过1。如果某种操作(一般是插入和删除操作)破坏了这种平衡性,就要对树中不平衡的结点进行“旋转”来修复平衡性。
  • 本平衡二叉搜索树的实现和之前的二叉搜索树实现很类似,都是包含了自定义的比较器。它们实现的不同点主要体现在平衡二叉搜索树的插入和删除操作要相对复杂一点,因为在元素插入或删除后,如果树的平衡性被破坏了还要进行平衡性的修复。因为实现中需要频繁的计算左子树和右子树的高度差,所以我们在树中的节点里维护了一个表示当前结点的高度的变量。

  • 来看这棵树:

                                                                       

                                                                                             图1

            数字9这棵树的左子树的高度为2,没有右子树,所以差的绝对值为2,可知整个5这棵树不是一棵平衡二叉树;

 

  • 再来看这棵树:

                                                               

                                                                                            图2

             这棵树把7旋转到9的位置,然后把整棵树变成了平衡二叉树;

3.概念:

  • 平衡因子:将二叉树上节点的左子树高度减去右子树高度的值称为该节点的平衡因子BF(Balance Factor)。
  • 平衡二叉树就是一棵二叉树上所有节点的平衡因子的绝对值小于1的树

以图2为例:

  •     节点5的左子树高度为3,右子树高度为3,BF= 3-3 = 0;
  •     节点3的左子树高度为2,右子树高度为1,BF= 2-1 = 1;
  •     节点1的左子树高度为0,右子树高度为1,BF= 0-1 = -1;
  •     节点4的左子树高度为0,右子树高度为0,BF= 0-1 = 0;
  •     以此类推

最小不平衡子树:距离插入节点最近的,且平衡因子的绝对值大于1的节点为根的子树.

 

首先我们先讲解一下平衡调整规则:       

1.LL型调整:

由于在A的左孩子(L)的左子树(L)上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由1增至2。下面图1是LL型的最简单形式。显然,按照大小关系,结点B应作为新的根结点,其余两个节点分别作为左右孩子节点才能平衡,A结点就好像是绕结点B顺时针旋转一样。

 

LL型调整的一般形式如下图2所示,表示在A的左孩子B的左子树BL(不一定为空)中插入结点(图中阴影部分所示)而导致不平衡( h 表示子树的深度)。这种情况调整如下:①将A的左孩子B提升为新的根结点;②将原来的根结点A降为B的右孩子;③各子树按大小关系连接(BL和AR不变,BR调整为A的左子树)。

                                     

代码实现:

BTNode *ll_rotate(BTNode *y)
{
    BTNode *x = y->left;
    y->left = x->right;
    x->right = y;   
 
    y->height = max(height(y->left), height(y->right)) + 1;
    x->height = max(height(x->left), height(x->right)) + 1;
 
    return x;
}


 
2. RR型调整:

由于在A的右孩子(R)的右子树(R)上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由-1变为-2。图3是RR型的最简单形式。显然,按照大小关系,结点B应作为新的根结点,其余两个节点分别作为左右孩子节点才能平衡,A结点就好像是绕结点B逆时针旋转一样。

 

RR型调整的一般形式如下图4所示,表示在A的右孩子B的右子树BR(不一定为空)中插入结点(图中阴影部分所示)而导致不平衡( h 表示子树的深度)。这种情况调整如下:

将A的右孩子B提升为新的根结点;
将原来的根结点A降为B的左孩子
各子树按大小关系连接(AL和BR不变,BL调整为A的右子树)。


                                                                     图4  一般形式的RR型调整

代码实现:

BTNode *rr_rotate(struct Node *y)
{
    BTNode *x = y->right;
    y->right = x->left;
    x->left = y;
    
 
    y->height = max(height(y->left), height(y->right)) + 1;
    x->height = max(height(x->left), height(x->right)) + 1;
 
    return x;
}


 
3. LR型调整:

由于在A的左孩子(L)的右子树(R)上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由1变为2。图5是LR型的最简单形式。显然,按照大小关系,结点C应作为新的根结点,其余两个节点分别作为左右孩子节点才能平衡。

                                                          

LR型调整的一般形式如下图6所示,表示在A的左孩子B的右子树(根结点为C,不一定为空)中插入结点(图中两个阴影部分之一)而导致不平衡( h 表示子树的深度)。这种情况调整如下:①将B的右孩子C提升为新的根结点;②将原来的根结点A降为C的右孩子;③各子树按大小关系连接(BL和AR不变,CL和CR分别调整为B的右子树和A的左子树)。

                                                                      图6  一般形式的LR型调整

3.1 代码实现:

BTNode* lr_rotate(BTNode* y)
{
    BTNode* x = y->left;
    y->left = rr_rotate(x);
    return ll_rotate(y);
}


4. RL型调整:

由于在A的右孩子(R)的左子树(L)上插入新结点,使原来平衡二叉树变得不平衡,此时A的平衡因子由-1变为-2。图7是RL型的最简单形式。显然,按照大小关系,结点C应作为新的根结点,其余两个节点分别作为左右孩子节点才能平衡。


                                                         图7  最简单的RL型调整

RL型调整的一般形式如下图8所示,表示在A的右孩子B的左子树(根结点为C,不一定为空)中插入结点(图中两个阴影部分之一)而导致不平衡( h 表示子树的深度)。这种情况调整如下:①将B的左孩子C提升为新的根结点;②将原来的根结点A降为C的左孩子;③各子树按大小关系连接(AL和BR不变,CL和CR分别调整为A的右子树和B的左子树)。


                                                                             图8  一般形式的RL型调整

4.1 代码实现

Node* rl_rotate(Node* y)
{
    Node * x = y->right;
    y->right = ll_rotate(x);
    return rr_rotate(y);
}

                                              

4.实现:

通过平衡二叉树的性质创建,而不是创建普通的二叉排序树

如果给定一个数组     arr[3,2,1,4,5,6,7,10,9,8]

二叉排序树:                                                                                        平衡二叉树:

                                                                        

创建过程:

                    旋转后->                

  • 3->2->1,当进行到1之后,可以看出节点3的左子树高度为2,右子树高度为0,BF为2-0=0;所以要进行LL旋转
  • 旋转后->
  • 当到节点5的时候右边不平衡了,此时要进行RR调整

                                                

  • 当插入到6的时候又不平衡了,对于节点2的BF=2,所以进行RR调整:但是你会发现出现中间图的样子,这时应该对节点3进行抛弃,事实上一棵树右子树最左边的节点可以放在这棵树左子树最右边的位置,就有了第三张图的样子;
  •                  旋转->
  • 插入7,不平衡,进行RR调整,

 旋转后->         

  • 插入10和9后不平衡,节点7进行RL调整

        

  • 此时最后一个节点8插入,最小不平衡子树为6

         旋转后->      

 

这样我们的平衡二叉树就构建完了;

树与二叉树:https://blog.csdn.net/alzzw/article/details/97283324

线索二叉树:https://blog.csdn.net/alzzw/article/details/97423394
哈夫曼树:https://blog.csdn.net/alzzw/article/details/97809047

二叉搜索(排序)树;https://blog.csdn.net/alzzw/article/details/97563011
B树~B+树:https://blog.csdn.net/alzzw/article/details/97633941
红黑树:https://blog.csdn.net/alzzw/article/details/97770753

下面是完整的代码实现:包括(插入,删除及遍历)

#include<stdio.h>
#include<stdlib.h>
 
typedef struct Node
{
    int key;
    struct Node *left;
    struct Node *right;
    int height;
}BTNode;
 
int max(int a, int b);
 
 
int height(struct Node *N)
{
    if (N == NULL)
        return 0;
    return N->height;
}
 
int max(int a, int b)
{
    return (a > b) ? a : b;
}
 
BTNode* newNode(int key)
{
    struct Node* node = (BTNode*)malloc(sizeof(struct Node));
    node->key = key;
    node->left = NULL;
    node->right = NULL;
    node->height = 1;
    return(node);
}
 
BTNode* ll_rotate(BTNode* y)
{
    BTNode *x = y->left;
    y->left = x->right;
    x->right = y;
 
    y->height = max(height(y->left), height(y->right)) + 1;
    x->height = max(height(x->left), height(x->right)) + 1;
 
    return x;
}
 
BTNode* rr_rotate(BTNode* y)
{
    BTNode *x = y->right;
    y->right = x->left;
    x->left = y;
 
    y->height = max(height(y->left), height(y->right)) + 1;
    x->height = max(height(x->left), height(x->right)) + 1;
 
    return x;
}
 
int getBalance(BTNode* N)
{
    if (N == NULL)
        return 0;
    return height(N->left) - height(N->right);
}
 
BTNode* insert(BTNode* node, int key)
{
 
    if (node == NULL)
        return newNode(key);
 
    if (key < node->key)
        node->left = insert(node->left, key);
    else if (key > node->key)
        node->right = insert(node->right, key);
    else
        return node;
 
    node->height = 1 + max(height(node->left), height(node->right));
 
 
    int balance = getBalance(node);
 
 
 
    if (balance > 1 && key < node->left->key) //LL型
        return ll_rotate(node);
 
 
    if (balance < -1 && key > node->right->key)     //RR型
        return rr_rotate(node);
 
 
    if (balance > 1 && key > node->left->key)     //LR型
    {
        node->left = rr_rotate(node->left);
        return ll_rotate(node);
    }
 
    if (balance < -1 && key < node->right->key)     //RL型
    {
        node->right = ll_rotate(node->right);
        return rr_rotate(node);
    }
 
    return node;
}
 
 
BTNode * minValueNode(BTNode* node)
{
    BTNode* current = node;
 
    while (current->left != NULL)
        current = current->left;
 
    return current;
}
 
BTNode* deleteNode(BTNode* root, int key)
{
 
    if (root == NULL)
        return root;
 
    if (key < root->key)
        root->left = deleteNode(root->left, key);
 
    else if (key > root->key)
        root->right = deleteNode(root->right, key);
 
    else
    {
        if ((root->left == NULL) || (root->right == NULL))
        {
            BTNode* temp = root->left ? root->left : root->right;
 
            if (temp == NULL)
            {
                temp = root;
                root = NULL;
            }
            else
                *root = *temp;
            free(temp);
        }
        else
        {
            BTNode* temp = minValueNode(root->right);
 
            root->key = temp->key;
 
            root->right = deleteNode(root->right, temp->key);
        }
    }
 
 
    if (root == NULL)
        return root;
 
    root->height = 1 + max(height(root->left), height(root->right));
 
    int balance = getBalance(root);
 
 
    if (balance > 1 && getBalance(root->left) >= 0) //LL型
        return ll_rotate(root);
 
 
    if (balance > 1 && getBalance(root->left) < 0) //LR型
    {
        root->left = rr_rotate(root->left);
        return ll_rotate(root);
    }
 
    if (balance < -1 && getBalance(root->right) <= 0) //RR型
        return rr_rotate(root);
 
    if (balance < -1 && getBalance(root->right) > 0)  //Rl型
    {
        root->right = ll_rotate(root->right);
        return rr_rotate(root);
    }
 
    return root;
}
 
 
void preOrder(struct Node *root)
{
    if (root != NULL)
    {
        printf("%d ", root->key);
        preOrder(root->left);
        preOrder(root->right);
    }
}
 
int main()
{
    BTNode *root = NULL;
 
    root = insert(root, 9);
    root = insert(root, 5);
    root = insert(root, 10);
    root = insert(root, 0);
    root = insert(root, 6);
    root = insert(root, 11);
    root = insert(root, -1);
    root = insert(root, 1);
    root = insert(root, 2);
    printf("前序遍历:\n");
    preOrder(root);
 
    /* The constructed AVL Tree would be
                     9
                    /  \
                   1    10
                 /  \     \
                0    5     11
               /    /  \
              -1   2    6
    */
    
    root = deleteNode(root, 10);
    /* The AVL Tree after deletion of 10
                       1
                     /   \
                    0     9
                  /     /  \
                -1     5     11
                     /  \
                    2    6
    */
    printf("\n");
    printf("前序遍历:\n");
    preOrder(root);
    return 0;
}

 

参考: 

https://blog.csdn.net/isunbin/article/details/81707606

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿尔兹

如果觉得有用就推荐给你的朋友吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值