首页 > 代码库 > AVL树
AVL树
一. 介绍
在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。AVL树得名于它的发明者G.M. Adelson-Velsky和E.M. Landis,他们在1962年的论文《An algorithm for the organization of information》中发表了它。
节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。
AVL树的节点定义:
struct AVLNode{ int data; Node* left; Node* right; char height; Node(int _data=http://www.mamicode.com/0):data(_data),left(0),right(0){}};
这里用char来存储高度, 而不是用bool存储高度差是很合理的. 一方面, 用bool存储高度差并不能节省空间, 因为现代计算机的最小寻址单元是1byte, 小于1byte的变量会被对齐到1byte. 另一方面, 由于avl树是自平衡的, 因此当高度达到char的最大值127时, avl树的节点数已经大于2^127-1个, 这已经很庞大了, 在一般需求中无需用更大的数据类型来存储高度.
二. AVL的核心: 旋转操作
AVL树的旋转一共有四种情形,注意所有旋转情况都是围绕着使得二叉树不平衡的第一个节点(B)展开的。
1. LL型失衡
B为左孩子, 且B的左孩子高于右孩子
2. RR型失衡
B为右孩子, 且B的右孩子高于左孩子
3. LR型失衡
B为左孩子, 且B的右孩子高于左孩子。这时需要旋转两次,仅一次的旋转是不能够使二叉树再次平衡。如图所示,在B节点按照RR型向左旋转一次之后,二叉树在A节点仍然不能保持平衡,这时还需要再向右旋转一次。
4. RL型失衡
B为右孩子, 且B的左孩子高于右孩子。同样,这时需要旋转两次,旋转方向刚好同LR型相反。
void AVLTree::rotateLL(AVLNode*& a){ AVLNode *b = a->left; a->left = b->right; b->right = a; a->height = max(getHeight(a->left), getHeight(a->right)) + 1; b->height = max(getHeight(a->left), getHeight(a->right)) + 1; a = b;}void AVLTree::rotateRR(AVLNode*& a){ AVLNode *b = a->right; a->right = b->left; b->left = a; a->height = max(getHeight(a->left), getHeight(a->right)) + 1; b->height = max(getHeight(a->left), getHeight(a->right)) + 1; a = b;}void AVLTree::rotateLR(AVLNode*& a){ rotateRR(a->left); rotateLL(a);}void AVLTree::rotateRL(AVLNode*& a){ rotateLL(a->right); rotateRR(a);}AVLNode* AVLTree::search(DataType x, AVLNode*& father){ AVLNode *p = root; father = NULL; while (p != NULL&&x != p->data) { father = p; if (x > p->data) p = p->right; else p = p->left; } return p;}char AVLTree::getHeight(AVLNode *x){ if (!x) return -1; //return max(getHeight(x->left), getHeight(x->right)) + 1; char l = (x->left) ? x->left->height : -1; char r = (x->right) ? x->right->height : -1; return max(l, r) + 1;}
三. AVL的插入
AVL的插入是二叉搜索树的插入的扩展: 在回朔阶段添加了检查平衡性的步骤. 在实际执行时, 首先按照插入二叉搜索树的方法(自顶向下)将新节点插入到空指针位置, 从而形成一个新的叶子节点. 然后在回朔阶段, 自底向上地检查平衡性. 当高度差为2时, 通过旋转操作来调整平衡.
每次检查平衡性之前, 一个必要的步骤就是计算当前结点的高度. 否则就无法检查平衡性.旋转完成之后仍然要计算节点高度, 以保证下一层回朔的正确性
void AVLTree::insertAVL(DataType x){ insertAVL(x, root);}void AVLTree::insertAVL(DataType x, AVLNode*& node){ if (!node) { node = new AVLNode(x); return; } if (x < node->data) { insertAVL(x, node->left); node->height = max(getHeight(node->left), getHeight(node->right)) + 1; // 如果x比当前结点小, 那么只可能在该节点导致LL或LR失衡 if (getHeight(node->left) - getHeight(node->right) == 2) { if (x < node->left->data) //x插入到左孩子的左侧, 导致LL失衡 rotateLL(node); else if (x > node->left->data) //x插入到左孩子的右侧, 导致LR失衡 rotateLR(node); } } else if (x > node->data) { insertAVL(x, node->right); node->height = max(getHeight(node->left), getHeight(node->right)) + 1; // 类比上面的分析 if (getHeight(node->left) - getHeight(node->right) == -2) { if (x > node->right->data) rotateRR(node); else if (x < node->right->data) rotateRL(node); } } node->height = max(getHeight(node->left), getHeight(node->right)) + 1;}
AVL树