简介
平衡树(AVLTree)
在计算机科学 中,AVL树 是最先发明的自平衡二叉查找树 。在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树 。查找、插入和删除在平均和最坏情况下的时间复杂度 都是 。增加和删除可能需要通过一次或多次树旋转 来重新平衡这个树。AVL树得名于它的发明者G. M. Adelson-Velsky 和E. M. Landis ,他们在1962年的论文《An algorithm for the organization of information》中发表了它。
节点的平衡因子 是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。
二叉查找树给我们带来了很多方便,但是由于其在有序序列插入时就会退化成单链表(时间复杂度退化成 O(n),AVL-tree就克服了上述困难。AVL-tree是一个“加上了平衡条件的”二叉搜索树 ,平衡条件确保整棵树的深度为O(log n)。
AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是 O(log n) 。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。
AVL树的所有操作都与二叉查找树相同,不同的是,这里AVL树需要做“AVL旋转”。
AVL旋转
AVL树最重要的核心部分就是AVL旋转了,这部分我的感触是,单做旋转还是挺好理解的,只不过写起代码来有点复杂,书中以插入节点为例,删除节点的部分折腾了好久。
在理解AVL旋转之前,首先得知道以下几个概念:
AVL 树节点的插入总是在叶子节点。
AVL 树在插入节点之前总是满足平衡条件的。
插入新节点后有可能满足平衡条件也有可能不满足。
当不满足平衡条件后,我们就需要对新的树进行旋转。
旋转之前,我们首先要找到一个X节点,这个X节点做如下定义:
假如我们在某一个叶子节点处插入一个新的节点后,此时这棵树的某些节点的平衡性会发生变化,那么我们从叶子节点向上到根节点的路径上第一个平衡性发生变化的节点。
基于这个X节点,考虑一件事情:
这个X节点分为左右子树,左右子树又有左右子树,1分2,2分4,所以以这个X节点为根节点的话,新插入的节点可能出现的位置有:
X的左孩子节点的左子树上**(left-left)**
X的右孩子节点的右子树上**(right-right)**
X的左孩子节点的右子树上**(left-right)**
X的右孩子节点的左子树上**(right-left)**
根据上述情况就延生出了4种旋转:
1.left-left Rotation
2.right-right Rotation
3.left-right Rotation
4.right-left Rotation
前两种属于单旋转,后两种属于双旋转,双旋转的操作可以由两次单旋转组成。
PS:AVL树的旋转还是得画图来理解,这里直接贴出书中的图了。
图片来自 C小加的博客
6节点的左子树3节点高度比右子树7节点大2,左子树3节点的左子树1节点高度大于右子树4节点,这种情况成为左左(LL)。
6节点的左子树2节点高度比右子树7节点大2,左子树2节点的左子树1节点高度小于右子树4节点,这种情况成为左右(LR)。
2节点的左子树1节点高度比右子树5节点小2,右子树5节点的左子树3节点高度大于右子树6节点,这种情况成为右左(RL)。
2节点的左子树1节点高度比右子树4节点小2,右子树4节点的左子树3节点高度小于右子树6节点,这种情况成为右右(RR)。
从图2中可以可以看出,1和4两种情况是对称的,这两种情况的旋转算法是一致的,只需要经过一次旋转就可以达到目标,我们称之为单旋转。2和3两种情况也是对称的,这两种情况的旋转算法也是一致的,需要进行两次旋转,我们称之为双旋转。
那么为什么需要双旋转呢?
这里我做出我个人的解释,在 LL 情况中,要达到平衡,是需要将失衡节点划分到右边,失衡节点的左孩子补上失衡节点的位置。这样左子树的高度 -1, 右边的高度 +1,这样左右两边的个数就平衡了。当然根据BST的性质,如果失衡节点存在右孩子的话应该划也要分到右边。RR 情况与 LL 情况对称。
而在 LR 情况中,我们是需要把失衡节点划到右边,失衡节点的左孩子的右孩子替补失衡节点原来的位置。但我们的节点存储结构有不能获得前驱节点的限制,我们只有后继关系,即我们只能通过失衡节点访问其他节点,所以不能直接把LR孩子放上来,而是分成两步调整。
// 这里的描述太那啥了,得搞点图说明下
AVL-Tree实现
AVL-Tree是一个二叉排序树,其基本操作也跟它类似,唯一需要注意的就是在插入,删除节点后,需要对树进行调整,让树的每个节点保持平衡。
节点的平衡因子是通过计算其左子树和右子树的差得来的,这里有两种考虑方式:
每次都计算一次(递归求深度)。
将平衡因子作为一个成员变量保存在节点中,平衡性发生变化的时候更新。
本文采取的是第一种方式,关于两种方式利弊的比较:
// 不想写?自己百度吧,反正就是第一种方法从上到下递归存在重复调用增加时间开销,第二种平衡性变化时候需要update 失衡位置 balanceFactor
另外,这里我用了C++类封装,为了学习还顺便使用了模板,所以类的声明和实现都放在了一个文件中,感觉内容太多,还是分开来比较好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #ifndef __AVLNODE_H__ #define __AVLNODE_H__ #include <iostream> #include <vector> #include <algorithm> template <typename KeyType>class AVLNode {public : KeyType key; AVLNode * left; AVLNode * right; AVLNode() : key(0 ), left(NULL ), right(NULL ) {} AVLNode(KeyType k) :key(k), left(NULL ), right(NULL ) {} }; #endif
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #ifndef AVLTREE_AVLTREE_H #define AVLTREE_AVLTREE_H #include "AVLNode.h" template <typename KeyType>class AVLTree { typedef AVLNode<KeyType> AVLNode; typedef AVLTree<KeyType> AVLTree; private : AVLNode * avlroot; int __height(const AVLNode *root); int __diff(const AVLNode*root); AVLNode * __ll_Rotation(AVLNode *root); AVLNode * __rr_Rotation(AVLNode *root); AVLNode * __lr_Rotation(AVLNode *root); AVLNode * __rl_Rotation(AVLNode *root); AVLNode * __Balance(AVLNode *root); AVLNode * __Insert(AVLNode *root, const KeyType &k); void __InorderTraversal(const AVLNode* root); void __InorderTraversal(const AVLNode*root, std ::vector <KeyType>&vec); bool __isLeaf(AVLNode* const &node) {return (node->left == nullptr && node->right == nullptr ) ? true : false }; bool __isNodeWithTwoChild(AVLNode * const &node); AVLNode* __search(AVLNode *const root, const KeyType &k); void __deleteTree(AVLNode * root); AVLNode* __Delete(AVLNode * root, const KeyType& k); AVLNode* __treeMin(AVLNode *root); AVLNode* __treeMax(AVLNode *root); public : AVLTree(){ avlroot = nullptr ; } ~AVLTree(); AVLTree(const std ::vector <KeyType>&); AVLTree(const KeyType * arr, size_t len); void InorderTraversal () ; void InorderTraversal (std ::vector <KeyType>&) ; bool Delete (const KeyType &k) ; void Insert (const KeyType & k) ; bool IsEmpty () { return avlroot == nullptr ; } bool search (const KeyType &k) ; }; #endif
旋转操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 template <typename KeyType>AVLNode * AVLTree::__ll_Rotation(AVLNode *root) { AVLNode * tmp; tmp = root->left; root->left = tmp->right; tmp->right = root; return tmp; } template <typename KeyType>AVLNode * AVLTree::__rr_Rotation(AVLNode *root) { AVLNode* tmp; tmp = root->right; root->right = tmp->left; tmp->left = root; return tmp; } template <typename KeyType>AVLNode * AVLTree::__lr_Rotation(AVLNode *root) { AVLNode * tmp; tmp = root->left; root->left = __rr_Rotation(tmp); return __ll_Rotation(root); } template <typename KeyType>AVLNode * AVLTree::__rl_Rotation(AVLNode *root) { AVLNode * tmp; tmp = root->right; root->right = __ll_Rotation(tmp); return __rr_Rotation(root); }
AVLTree 插入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 template <typename KeyType>AVLNode * AVLTree::__Insert(AVLNode * root, const KeyType& k) { if (nullptr == root) { root = new AVLNode(k); return root; } else if (k < root->key) { root->left = __Insert(root->left, k); root = __Balance(root); } else if (k>root->key) { root->right = __Insert(root->right, k); root = __Balance(root); } return root; }
AVLTree 删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 template <typename KeyType>AVLNode * AVLTree::__Delete(AVLNode *root, const KeyType& k) { if (nullptr == root) return root; if (!search(k)) { std ::cerr << "Delete error , key not find" << std ::endl ; return root; } if (k == root->key) { if (__isNodeWithTwoChild(root)) { if (__diff(root) > 0 ) { root->key = __treeMax(root->left)->key; root->left = __Delete(root->left, root->key); } else { root->key = __treeMin(root->right)->key; root->right = __Delete(root->right, root->key); } } else { AVLNode * tmp = root; root = (root->left) ? (root->left) :( root->right); delete tmp; tmp = nullptr ; } } else if (k < root->key) { root->left = __Delete(root->left, k); if (__diff(root) < -1 ) { if (__diff(root->right) > 0 ) { root = __rl_Rotation(root); } else { root = __rr_Rotation(root); } } } else { root->right = __Delete(root->right, k); if (__diff(root) > 1 ) { if (__diff(root->left) < 0 ) { root = __lr_Rotation(root); } else { root = __ll_Rotation(root); } } } return root; }
附:完整代码
参考
STL源码笔记(18)—平衡二叉树AVL(C++封装+模板)
平衡二叉树,AVL树之图解篇
一步一步写平衡二叉树(AVL树)
平衡二叉树(avl)分析与实现