AVL树

一棵AVL树是其每个节点的左子树和右子树的高度最多差1的二叉查找树。实际高度只比logN多以一点,和普通二叉查找树相比,平衡二叉搜索树一般而言搜寻时间可节省25%左右(STL源码剖析P203)。

只有那些从插入点到根节点的路径上的节点的平衡可能被改变,因为只有这些节点的子树可能发生变化。

把需要重新平衡的节点称为a(左右子树高度差大于1)。注意,确定这个节点很重要,否则无法确定是下列哪一种情况。
造成不平衡的4种插入情况:
  • 对a的左儿子的左子树进行一次插入:右单转,旋转主体是失衡节点和左孩子以及它们的连线。
  • 对a的右儿子的右子树进行一次插入:左单转,旋转主体是失衡节点和右孩子以及它们的连线。
  • 对a的左儿子的右子树进行一次插入:左-右双旋转,先在失衡节点的儿子和孙子之间旋转而后再在失衡节点和它的新儿子之间旋转。
  • 对a的右儿子的左子树进行一次插入:右-左双旋转,先在失衡节点的儿子和孙子之间旋转而后再在失衡节点和它的新儿子之间旋转。

来一张维基百科的图,图中有一个错误,后两种情况的Root因该标在树的根上:


《算法设计与分析基础》中的一句话:
如果有若干个节点的平衡因子大于1,先找出最靠近新插入的叶子的不平衡节点,然后再旋转以该节点为根的树。

在程序设计中,和二叉查找树一样,通常使用递归地方法实现AVL树。
部分代码如下:
#include <stdio.h>
 
typedef int ElementType;
typedef struct AvlNode *Position;
typedef struct AvlNode *AvlTree;
 
struct AvlNode {
    ElementType Element;
    AvlTree Left;
    AvlTree Right;
    int Height;
};
 
int Max(int x, int y)
{
    return x > y ? x : y;
}
 
// 获得节点高度
int Height(Position p)
{
    if (p == NULL)
        return -1;
    else
        return p->Height;
}
 
// T跟左儿子进行右旋转
Position RightSingleRotate(Position T)
{
    Position NewRoot;
 
    NewRoot = T->Left;
    T->Left = NewRoot->Right;
    NewRoot->Right = T;
 
    // 修改高度
    T->Height = Max(Height(T->Left), Height(T->Right)) + 1;
    NewRoot->Height = Max(Height(NewRoot->Left), T->Height) + 1;
 
    return NewRoot;
}
 
// T跟右儿子进行左旋转
Position LeftSingleRotate(Position T)
{
    Position NewRoot;
 
    NewRoot = T->Right;
    T->Right = NewRoot->Left;
    NewRoot->Left = T;
 
    // 修改高度
    T->Height = Max(Height(T->Left), Height(T->Right)) + 1;
    NewRoot->Height = Max(Height(NewRoot->Right), T->Height) + 1;
 
    return NewRoot;
}
 
Position DoubleRotateWithLeft(Position T)
{
    // 先左旋转
    T->Left = LeftSingleRotate(T->Left);
    // 再右旋转
    return RightSingleRotate(T);
}
 
Position DoubleRotateWithRight(Position T)
{
    // 先右旋转
    T->Right = RightSingleRotate(T->Right);
    // 再左旋转
    return LeftSingleRotate(T);
}
 
AvlTree Insert(AvlTree T, ElementType x)
{
    if (T == NULL)
    {
        T = malloc(sizeof(struct AvlNode));
        if (T == NULL)
            return -1;
 
        T->Element = x;
        T->Left = T->Right = NULL;
        T->Height = 0;
    }
    else if (x < T->Element)
    {
        T->Left = Insert(T->Left, x);
        if (Height(T->Left) - Height(T->Right) == 2)
            if (x < T->Left->Element)
                T = RightSingleRotate(T);       // 左-左:右单旋转
            else
                T = DoubleRotateWithLeft(T);    // 左-右:左右双旋转
 
    }
    else if (x > T->Element)
    {
        T->Right = Insert(T->Right, x);
        if (Height(T->Right) - Height(T->Left) == 2)
            if (x > T->Right->Element)
                T = LeftSingleRotate(T);        // 右-右:左单旋转
            else
                T = DoubleRotateWithRight(T);   // 右-左:右左双旋转
    }
 
    T->Height = Max(Height(T->Left), Height(T->Right)) + 1; // 调整高度
    return T;
}
 
void MiddleTraversal(AvlTree T)
{
    if (T == NULL)
        return;
 
    MiddleTraversal(T->Left);
    printf("%d ", T->Element);
    MiddleTraversal(T->Right);
}
 
int main()
{
    AvlTree tree = NULL;
    int i;
 
    for (i = 1; i <= 16; i++)
        tree = Insert(tree, i);
 
    MiddleTraversal(tree);
 
    return 0;
}

参考:
《数据结构于算法分析》 P80.
《STL源码剖析》 P203.
AVL是一种自平衡的二叉查找,它确保了的高度始终保持在对数级别,从而保证了查找、插入和删除操作的时间复杂度为O(log n)。这种数据结构以它的发明者G.M. Adelson-Velsky和E.M. Landis的名字命名[^1]。 ### AVL的定义 - **平衡因子**:每个节点都有一个平衡因子,它是该节点左子的高度减去右子的高度。对于AVL来说,所有节点的平衡因子只能是-1, 0或1。 - **空**:如果T是空,则它自然是一个AVL。 - **非空**:若T不是空,则其左右子TL和TR都必须是AVL,并且对于任意节点,|HL - HR| ≤ 1(其中HL和HR分别表示左子和右子的高度)。 ### AVL的操作 当进行插入或删除操作时,可能会破坏AVL的平衡性,这时需要通过旋转来重新恢复平衡: - **单旋转**: - 左单旋(Single Rotate with Left)用于处理左孩子的左子过高。 - 右单旋(Single Rotate with Right)用于处理右孩子的右子过高。 - **双旋转**: - 左右双旋(Double Rotate with Left)用于处理左孩子的右子过高的情况。 - 右左双旋(Double Rotate with Right)用于处理右孩子的左子过高的情况。 这些旋转操作可以保持AVL的性质不变,并且能够快速地调整的结构以维持平衡。 ### AVL的实现 下面是一个简单的C语言代码示例,展示了如何定义AVL的节点以及实现基本的插入操作。这个例子中包含了必要的函数声明和一些核心逻辑。 ```c #include <stdio.h> #include <stdlib.h> // 定义AVL节点结构体 typedef struct AvlNode { int element; struct AvlNode *left; struct AvlNode *right; int height; // 节点的高度 } *AvlTree, *Position; // 获取两个整数中的较大者 int max(int a, int b) { return (a > b) ? a : b; } // 计算给定节点的高度 static int Height(AvlTree T) { if (T == NULL) return -1; // 空节点高度为-1 else return T->height; } // 单旋转 - 左左情况 AvlTree singlerotatewithLeft(AvlTree K2) { Position K1 = K2->left; K2->left = K1->right; K1->right = K2; // 更新节点高度 K2->height = max(Height(K2->left), Height(K2->right)) + 1; K1->height = max(Height(K1->left), K2->height) + 1; return K1; // 新根 } // 单旋转 - 右右情况 AvlTree singlerotatewithRight(AvlTree K2) { Position K1 = K2->right; K2->right = K1->left; K1->left = K2; // 更新节点高度 K2->height = max(Height(K2->left), Height(K2->right)) + 1; K1->height = max(Height(K1->right), K2->height) + 1; return K1; // 新根 } // 双旋转 - 左右情况 AvlTree doublerotatewithLeft(AvlTree K3) { K3->left = singlerotatewithRight(K3->left); return singlerotatewithLeft(K3); } // 双旋转 - 右左情况 AvlTree doublerotatewithRight(AvlTree K3) { K3->right = singlerotatewithLeft(K3->right); return singlerotatewithRight(K3); } // 插入新元素到AVLAvlTree Insert(int x, AvlTree T) { if (T == NULL) { // 如果为空,创建新节点 T = (AvlTree)malloc(sizeof(struct AvlNode)); if (T == NULL) printf("Out of space!!!"); else { T->element = x; T->left = T->right = NULL; T->height = 0; // 新叶节点高度为0 } } else if (x < T->element) { // 向左子插入 T->left = Insert(x, T->left); // 检查并修复平衡 if (Height(T->left) - Height(T->right) == 2) { if (x < T->left->element) T = singlerotatewithLeft(T); // 左左旋转 else T = doublerotatewithLeft(T); // 左右旋转 } } else if (x > T->element) { // 向右子插入 T->right = Insert(x, T->right); // 检查并修复平衡 if (Height(T->right) - Height(T->left) == 2) { if (x > T->right->element) T = singlerotatewithRight(T); // 右右旋转 else T = doublerotatewithRight(T); // 右左旋转 } } // 更新高度 T->height = max(Height(T->left), Height(T->right)) + 1; return T; } ``` 上述代码提供了AVL的基本框架,包括节点定义、插入操作及必要的旋转方法。实际应用中可能还需要添加更多的功能,如删除节点、查找特定值等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值