AVL树

1.什么是AVL树

          AVL树是带有平衡条件的二叉查找树,是其每个结点的左子树和右子树的高度差最多差1的二叉查找树。如图1,左边的树是AVL树,但右边的不是。


图1 左边的是AVL树,右边的不是


2.AVL树的基本操作

         当向一棵AVL树中插入一个结点时,可能破坏AVL树的特性(例如,将6插入图1的AVL树中将会破坏项为8的结点平衡条件)。因此,我们需要对树进行简单的修正来恢复AVL树的平衡,这种修正操作叫做旋转(rotation)。在插入以后,只有那些从插入点到根节点的路径上的结点的平衡可能被破坏,因为只有这些结点的子树可能发生变化,在这条路径上可以发现一个结点,它的新平衡破坏了AVL条件,可以证明,恢复这个结点的平衡就可以保证整个树的AVL性质。

         我们把必须平衡的结点叫做a。则AVl树的不平衡可能是由以下四种情形造成的:

         (1)对a的左儿子的左子树进行一次插入

         (2)对a的左儿子的右子树进行一次插入

         (3)对a的右儿子的左子树进行一次插入

         (4)对a的右儿子的右子树进行一次插入

         对于(1)和(4),可以通过单旋转完成AVL树的修正,对于(2)和(3),可以通过双旋转来完成AVL树的修正。以下详细介绍单旋转和双旋转。

1.1单旋转

         图2显示了向右单旋转如何调整情形(1),其中k2表示插入后不平衡结点a。


图2 向右单旋转修正情形(1)

          图3显示了在将6插入左边原始的AVL树后结点8便不再平衡,于是,我们在7和8之间做一次单旋转,结果得到右边的树。


图3 插入6破坏了AVL性质,而后经过单旋转又将AVL性质恢复

           图4说明了向左单旋转如何调整情形(4),与情形(1)对称。


图4 向左单旋转修正情形(4)

         图5中,左边的图是在原来的AVL树中加入结点7,结点5不再平衡,破坏了AVL特性。右边的图是经过单旋转修正之后的树。


图5 插入7破坏了AVL性质,而后经过单旋转又将AVL性质恢复

1.2双旋转

         图6说明了如何通过左-右双旋转修正情形(2),先将以k1为根的子树向左单旋转,再将已k3为根的子树向右单旋转,可以得到右边的树。


图6 通过左-右双旋转修正情形(2)

         图7说明了如何通过右-左双旋转修正情形(4),先将以k3为根的子树向右单旋转,再将已k1为根的子树向左单旋转,可以得到右边的树。


图7 通过右-左双旋转修正情形(4)

          图8中,左边的图中,在原来的AVL树中加入结点14,结点6失去平衡,属于情形(4),右边的图式经过右-左双旋转修正之后的树。


图8 插入结点14,经过右-左双旋转之后的树

3.代码实现

           AVL树定义如下:

[cpp]  view plain copy
  1. typedef int ElemType;  
  2.   
  3. struct AvlNode  
  4. {  
  5.     ElemType elem;  
  6.     struct AvlNode *left;  
  7.     struct AvlNode *right;  
  8.     int height;   //树节点高度  
  9. };  
  10.   
  11. typedef struct AvlNode * AvlTree;//AvlTree  
            AVL树中插入操作如下:

[cpp]  view plain copy
  1. AvlTree insert(AvlTree tree, ElemType value)  
  2. {  
  3.     if(!tree)//添加新结点  
  4.     {  
  5.         tree = (AvlTree)malloc(sizeof(AvlNode));  
  6.         if(!tree)  
  7.             exit(EXIT_FAILURE);  
  8.         tree->elem = value;  
  9.         tree->left = tree->right = NULL;  
  10.         tree->height = 0;  
  11.     }  
  12.     else if(value < tree->elem)//在左子树中添加元素  
  13.     {  
  14.         tree->left = insert(tree->left, value);//递归插入,类似于二叉查找树  
  15.         if(getHeight(tree->left) - getHeight(tree->right) == 2)//失去平衡  
  16.             if(value < tree->left->elem)       //情形(1),向右单旋转  
  17.                 tree = singleRotateWithLeft(tree);  
  18.             else                    //情形(2),右-左双旋转  
  19.                 tree = doubleRotateWithLeft(tree);  
  20.     }  
  21.     else if(value > tree->elem)//在右子树中添加元素  
  22.     {  
  23.         tree->right = insert(tree->right, value);  
  24.         if(getHeight(tree->right) - getHeight(tree->left) == 2)//失去平衡  
  25.             if(value > tree->right->elem)      //情形(4),向左单旋转  
  26.                 tree = singleRotateWithRight(tree);  
  27.             else                    //情形(3),左-右双旋转  
  28.                 tree = doubleRotateWithRight(tree);  
  29.     }  
  30.     else//数据元素重复,什么也不做  
  31.         ;  
  32.     tree->height = max(getHeight(tree->left), getHeight(tree->right)) + 1;  
  33.     return tree;  
  34. }  
         向右单旋转代码如下:
[cpp]  view plain copy
  1. AvlTree singleRotateWithLeft(AvlTree k2)  
  2. {  
  3.     AvlTree k1;  
  4.   
  5.     k1 = k2->left;  
  6.     k2->left = k1->right;  
  7.     k1->right = k2;  
  8.   
  9.     k2->height = max(getHeight(k2->left), getHeight(k2->right)) + 1;  
  10.     k1->height = max(getHeight(k1->left), k2->height) + 1;  
  11.   
  12.     return k1;  
  13. }  

          向左单旋转代码如下:

[cpp]  view plain copy
  1. AvlTree singleRotateWithRight(AvlTree k1)  
  2. {  
  3.     AvlTree k2;  
  4.   
  5.     k2 = k1->right;  
  6.     k1->right = k2->left;  
  7.     k2->left = k1;  
  8.   
  9.     k1->height = max(getHeight(k1->left), getHeight(k1->right)) + 1;  
  10.     k2->height = max(k1->height, getHeight(k2->right)) + 1;  
  11.     return k2;  
  12. }  

           左-右双旋转代码如下:

[cpp]  view plain copy
  1. AvlTree doubleRotateWithLeft(AvlTree k3)  
  2. {  
  3.     singleRotateWithRight(k3->left);  
  4.     return singleRotateWithLeft(k3);  
  5. }  

          右-左双旋转代码如下:

[cpp]  view plain copy
  1. AvlTree doubleRotateWithLeft(AvlTree k1)  
  2. {  
  3.     singleRotateWithLeft(k1->right);  
  4.     return singleRotateWithRight(k1);  
  5. }  
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、付费专栏及课程。

余额充值