AVL 平衡树
AVL是一种平衡二叉树,它通过对二叉搜索树中的节点进行旋转使得二叉搜索树达到平衡。AVL在所有的平衡二叉搜索树中具有最高的平衡性。
定义
平衡二叉树或者为空树或者为满足如下性质的二叉搜索树:
- 左右子树的高度之差绝对值不超过1
- 左右子树仍然为平衡二叉树
定义平衡因子 BF(x) = x的左子树高度 - x的右子树的高度。平衡二叉树的每个节点的平衡因子只能为-1, 0, 1.
维持平衡思想
若二叉树当前为平衡状态,此时插入/删除一个新的节点,此时有可能造成二叉树不满足平衡条件,此时需要通过对节点进行旋转来使得二叉树达到平衡状态。从被插入/删除的节点开始向上查找,找到第一个不满足 |BF| <= 1的祖先节点P,此时对P和P的子节点(或P的子节点的子节点)进行旋转(可能分为下面的四种情况)。
旋转
(1)LL型平衡旋转法
由于在A的左孩子B的左子树上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行一次顺时针旋转操作。 即将A的左孩子B向右上旋转代替A作为根结点,A向右下旋转成为B的右子树的根结点。而原来B的右子树则变成A的左子树。
(2)RR型平衡旋转法
由于在A的右孩子C 的右子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行一次逆时针旋转操作。即将A的右孩子C向左上旋转代替A作为根结点,A向左下旋转成为C的左子树的根结点。而原来C的左子树则变成A的右子树。
(3)LR型平衡旋转法
由于在A的左孩子B的右子数上插入结点F,使A的平衡因子由1增至2而失去平衡。故需进行两次旋转操作(先逆时针,后顺时针)。即先将A结点的左孩子B的右子树的根结点D向左上旋转提升到B结点的位置,然后再把该D结点向右上旋转提升到A结点的位置。即先使之成为LL型,再按LL型处理。
如图中所示,即先将圆圈部分先调整为平衡树,然后将其以根结点接到A的左子树上,此时成为LL型,再按LL型处理成平衡型。
(4)RL型平衡旋转法
由于在A的右孩子C的左子树上插入结点F,使A的平衡因子由-1减至-2而失去平衡。故需进行两次旋转操作(先顺时针,后逆时针),即先将A结点的右孩子C的左子树的根结点D向右上旋转提升到C结点的位置,然后再把该D结点向左上旋转提升到A结点的位置。即先使之成为RR型,再按RR型处理。
如图中所示,即先将圆圈部分先调整为平衡树,然后将其以根结点接到A的左子树上,此时成为RR型,再按RR型处理成平衡型。
实现(c++)
#include<iostream> using namespace std; #define MAX(a, b) a > b? a:b struct TreeNode{ int data; TreeNode* child[2]; int size; int height; int count; TreeNode(int val){ data = val; size = count = height = 1; child[0] = child[1] = NULL; } void Update(){ size = count; height = 1; if (child[0]){ size += child[0]->size; height = MAX(height, 1 + child[0]->height); } if (child[1]){ size += child[1]->size; height = MAX(height, 1 + child[1]->height); } } }; struct AVL{ TreeNode* root; AVL() :root(NULL){}; int GetHeight(TreeNode* node){ if (!node) return 0; return node->height; } int GetSize(TreeNode* node){ if (!node) return 0; return node->size; } //注意这里使用指针的引用 void Rotate(TreeNode*& node, int dir){ TreeNode* ch = node->child[dir]; node->child[dir] = ch->child[!dir]; ch->child[!dir] = node; node->Update(); node = ch; } void Maintain(TreeNode*& node){ if (!node){ return; } int bf = GetHeight(node->child[0]) - GetHeight(node->child[1]); if (bf >= -1 && bf <= 1){ return; } if (bf == 2){ int bf2 = GetHeight(node->child[0]->child[0]) - GetHeight(node->child[0]->child[1]); if (bf2 == 1){ //左左旋转 Rotate(node, 0); } else if (bf2 == -1){ //左右旋转 Rotate(node->child[0], 1); Rotate(node, 0); } } else if (bf == -2){ int bf2 = GetHeight(node->child[1]->child[0]) - GetHeight(node->child[1]->child[1]); if (bf2 == 1){ //右左旋转 Rotate(node, 1); } else if (bf2 == -1){ //右右旋转 Rotate(node->child[1], 0); Rotate(node, 1); } } } void Insert(TreeNode*& node, int val){ if (!node){ node = new TreeNode(val); } else if (node->data == val){ node->count++; } else{ int dir = node->data < val; Insert(node->child[dir], val); } //维持树的平衡 Maintain(node); //更新节点 node->Update(); } //注意参数为指针的引用 void Delete(TreeNode*& node, int val){ if (!node){ return; }else if (node->data == val){ if (node->child[0] && node->child[1]){ int dir = GetHeight(node->child[0]) < GetHeight(node->child[1]); //将子树中较高的那棵,旋转 Rotate(node, dir); //递归调用delete,直到叶子节点才进行真正的删除 Delete(node->child[! dir], val); } else{ TreeNode* tmp_node = NULL; if (node->child[0]){ tmp_node = node->child[0]; } else if (node->child[1]){ tmp_node = node->child[1]; } delete node; node = tmp_node; //使用引用 } } else{ int dir = node->data < val; Delete(node->child[dir], val); } Maintain(node); //维持平衡 node->Update(); //更新节点 } int GetKth(TreeNode* node, int k){ while (node){ if (! node->child[0]){ if (k <= node->count){ return node->data; } else{ k -= node->count; node = node->child[1]; } } else{ if (node->child[0]->size < k && node->child[0]->size + node->count >= k){ return node->data; } else if (node->child[0]->size > k){ node = node->child[0]; } else{ k -= (node->child[0]->size + node->count); node = node->child[1]; } } } return -1; } };