手把手教,手写AVL树
作者:互联网
一、平衡二叉树之AVL树简介
平衡二叉树相信大家都不陌生了,它是一颗平衡的二叉排序树,它的查询、插入删除的时间复杂度均为O(logn),是非常好且重要的动态查找结构,因此被广泛的应用。
而AVL树是一种高度平衡的平衡二叉树,树中的任意节点的左右子树的高度之差不超过1。如果将二叉树上结点的平衡因子BF(Balanced Factor)定义为该结点的左子树与右子树的高度之差,根据AVL树的定义,AVL树中的任意结点的平衡因子只可能是-1(右子树高于左子树)、0或者1(左子树高于右子树),在某些图中也会表示为绝对高度差,即0,1,2这种形式,请注意理解。它通过树的旋转操作来维护平衡特性。下面我会逐步通过C++代码来理解、最终实现AVL树。
二、AVL树实现
1.二叉排序树实现
AVL树是一颗平衡的二叉排序树,所以为了实现AVL树,我们可以先从实现一颗普通的二叉排序树入手,实现一颗普通二叉排序树。为了接下来将二叉排序树优化为AVL树,代码中将树直接取名为AVL。
树中节点具有双亲和左右两个子节点的指针,
插入删除操作都是先查找到节点位置,再进行插入或者删除。
删除时,如果删除的是同时有左右子树的非叶子节点,则寻找该节点的后继节点,替换该节点再将后继节点删除,这里不做过多解释,代码如下:
#include<stdio.h> #include<malloc.h> struct AVLNode{ int value; int balance; //AVL树的平衡字段,普通二叉排序树并不需要用到该字段 AVLNode* left; AVLNode* right; AVLNode* parent; }; //这是一颗普通的二叉排序树,,命名为AVLTree仅仅因为方便起见 struct AVLTree{ AVLNode* root; }tree; //查询操作 struct AVLNode* select(int value,AVLNode* root){ if(root==0){ return 0; } if(root->value==value){ return root; } if(root->value>value){ if(root->left) return select(value,root->left); else return root; } if(root->value<value){ if(root->right) return select(value,root->right); else return root; } } //插入操作 void insert(int value,AVLNode* root){ AVLNode* node=select(value,root); if(node==0){ tree.root=(AVLNode*)malloc(sizeof(AVLNode)); tree.root->value=value; tree.root->left=tree.root->right=0; tree.root->parent=0; tree.root->balance=0; } else if(node->value!=value){ if(node->value>value){ node->left=(AVLNode*)malloc(sizeof(AVLNode)); node->left->value=value; node->left->left=node->left->right=0; node->left->parent=node; node->left->balance=0; }else if(node->value<value){ node->right=(AVLNode*)malloc(sizeof(AVLNode)); node->right->value=value; node->right->left=node->right->right=0; node->right->parent=node; node->right->balance=0; } } } void delnodeifhas1childornot(AVLNode* a){ if(a->parent==0){ if(a->left){ tree.root=a->left; a->left->parent=0; }else{ tree.root=a->right; a->right->parent=0; } }else{ if(a->parent->left==a){ if(a->left){ a->parent->left=a->left; a->left->parent=a->parent; }else{ a->parent->left=a->right; if(a->right) a->right->parent=a->parent; } }else{ if(a->left){ a->parent->right=a->left; a->left->parent=a->parent; }else{ a->parent->right=a->right; if(a->right) a->right->parent=a->parent; } } } } struct AVLNode* getmin(AVLNode* a){ if(a->left) getmin(a->left); else return a; } void delnodeifhas2child(AVLNode* a){ AVLNode* after=getmin(a->right); a->value=after->value; delnodeifhas1childornot(after); }
//删除操作 void del(int value,AVLNode* root){ AVLNode* node=select(value,root); if(node->value==value){ if(node->left&&node->right){ delnodeifhas2child(node); }else{ delnodeifhas1childornot(node); } } }
2.从二叉排序树到AVL树
2.1 旋转操作原理
AVL树通过左旋右旋来实现它的平衡性。理解左旋和右旋是AVL树实现的关键点,那么问题来了,所谓左旋右旋又具体是怎么样的操作呢?以右旋为例。
初始的AVL树有两个节点,a和b,此时树平衡。
插入节点c,若节点c小于b,则成为b的左子节点,此时a左子树深度为2,右子树深度为0,处于不平衡状态,此时执行右旋操作。
右旋就是使a、b进行右旋1/4圆,使得b节点作为父节点,a节点变为b节点的右节点,b的左子树在经过右旋操作后仍是b的左子树。
如果b有右子树。那么右旋后b的右子树将成为a的左子树。
这就是右旋操作的具体实现,左旋是右旋对称的过程。当二叉树插入或者删除一个节点时,如果破坏了树的平衡性,就会根据树的失衡情况进行旋转操作,如果左子树高于右子树,则右旋,如果右子树高于左子树,则左旋。
右旋及左旋的代码如下:
struct AVLNode* turnRight(AVLNode* a){ // 右旋使a的父节点的子节点替换为b AVLNode* b=a->left; if(a->parent!=0){ if(a->parent->right==a){ a->parent->right=b; }else{ a->parent->left=b; } } b->parent=a->parent; //将b的右子树作为a的左子树,并将a作为b的右子树 a->parent=b; a->left=b->right; if(a->left!=0) a->left->parent=a; b->right=a; //重新设置a、b节点的balance值 setBalance(a); setBalance(b); //返回b节点 return b; }
struct AVLNode* turnLeft(AVLNode* a){ //左旋把a的父节点的子节点替换为b AVLNode* b=a->right; if(a->parent!=0){ if(a->parent->right==a){ a->parent->right=b; }else{ a->parent->left=b; } } b->parent=a->parent; //将a作为b的左子树,并将b的左子树作为a的右子树 a->parent=b; a->right=b->left; b->left=a; if(a->right!=0) a->right->parent=a; //重新设置a\b的balance值 setBalance(a); setBalance(b);
//返回b节点
return b; }
int height(AVLNode* a){ if(a==0){ return 0; } int rightheight=height(a->right); int leftheight=height(a->left); return rightheight > leftheight ? (rightheight+1) : (leftheight+1); } void setBalance(AVLNode* a){ if(a) a->balance=height(a->right)-height(a->left); }
上面将树从失衡状态恢复到平衡状态只进行了一次旋转操作,事实上还有一种稍微复杂的情况,需要进行两次旋转才能将树重新恢复平衡
如果节点插入到a的左子树的右节点,则需要先左旋再右旋来重新平衡树。同理,如果节点插入到a的右子树的左子节点,则需要先右旋再左旋来重新平衡树。如果直接进行右旋,生成的树将仍然是失衡的。
代码如下,调用了上面实现的旋转操作:
struct AVLNode* turnLeftThenRight(AVLNode* a){ a->left = turnLeft(a->left); return turnRight(a); } struct AVLNode* turnRightThenLeft(AVLNode* a){ a->right = turnRight(a->right); return turnLeft(a); }
2.2 实现AVL树插入删除
AVL树的insert操作,仅仅在二叉排序树的insert基础上增加了rebalance操作。
//AVL树在insert或者del之后进行rebalance操作,从插入节点的父节点开始,逐层向上遍历进行rebalance,直到遍历到根节点,由于树是平衡的,所以rebalance的时间复杂度是O(logn)。 void rebalance(AVLNode* a){ setBalance(a); if(a->balance== -2){ //如果左子树比右子树高2层,需要通过旋转来重新平衡 if(a->left->balance <=0){ //如果左子节点的左子树比右子树高,则进行一次右旋;如果左子节点的右子树比左子树高,则先进行左旋,再进行右旋。 a=turnRight(a); }else{ a=turnLeftThenRight(a); } }else if(a->balance==2){ //如果右子树比左子树高2层,需要通过旋转来重新平衡 if(a->right->balance>=0){ //如果右子节点的右子树比左子树高,则进行一次左旋;如果右子节点的右子树比左子树低,则先进行右旋,再进行左旋。 a=turnLeft(a); }else{ a=turnRightThenLeft(a); } } if(a->parent){ rebalance(a->parent); }else{ tree.root=a; } } void insert(int value,AVLNode* root){ AVLNode* node=select(value,root); if(node==0){ tree.root=(AVLNode*)malloc(sizeof(AVLNode)); tree.root->value=value; tree.root->left=tree.root->right=0; tree.root->parent=0; tree.root->balance=0; } else if(node->value!=value){ if(node->value>value){ node->left=(AVLNode*)malloc(sizeof(AVLNode)); node->left->value=value; node->left->left=node->left->right=0; node->left->parent=node; node->left->balance=0; rebalance(node); //插入完成后进行rebalance }else if(node->value<value){ node->right=(AVLNode*)malloc(sizeof(AVLNode)); node->right->value=value; node->right->left=node->right->right=0; node->right->parent=node; node->right->balance=0; rebalance(node); //插入完成后进行rebalance } } }
AVL树的删除操作,也只是在二叉排序树的删除操作上,增加了rebalance操作。
void delnodeifhas1childornot(AVLNode* a){ if(a->parent==0){ if(a->left){ tree.root=a->left; a->left->parent=0; }else{ tree.root=a->right; a->right->parent=0; } }else{ if(a->parent->left==a){ if(a->left){ a->parent->left=a->left; a->left->parent=a->parent; }else{ a->parent->left=a->right; if(a->right) a->right->parent=a->parent; } }else{ if(a->left){ a->parent->right=a->left; a->left->parent=a->parent; }else{ a->parent->right=a->right; if(a->right) a->right->parent=a->parent; } } rebalance(a->parent); } } struct AVLNode* getmin(AVLNode* a){ if(a->left) getmin(a->left); else return a; } void delnodeifhas2child(AVLNode* a){ AVLNode* after=getmin(a->right); a->value=after->value; delnodeifhas1childornot(after); } void del(int value,AVLNode* root){ //删除操作 AVLNode* node=select(value,root); if(node->value==value){ if(node->left&&node->right){ delnodeifhas2child(node); }else{ delnodeifhas1childornot(node); } } }
完整AVL树c++代码如下:
#include<stdio.h> #include<malloc.h> struct AVLNode{ int value; int balance; AVLNode* left; AVLNode* right; AVLNode* parent; }; struct AVLTree{ AVLNode* root; }tree; int height(AVLNode* a){ if(a==0){ return 0; } int rightheight=height(a->right); int leftheight=height(a->left); return rightheight > leftheight ? (rightheight+1) : (leftheight+1); } struct AVLNode* select(int value,AVLNode* root){ if(root==0){ return 0; } if(root->value==value){ return root; } if(root->value>value){ if(root->left) return select(value,root->left); else return root; } if(root->value<value){ if(root->right) return select(value,root->right); else return root; } } void setBalance(AVLNode* a){ if(a) a->balance=height(a->right)-height(a->left); } struct AVLNode* turnLeft(AVLNode* a){ AVLNode* b=a->right; if(a->parent!=0){ if(a->parent->right==a){ a->parent->right=b; }else{ a->parent->left=b; } } b->parent=a->parent; a->parent=b; a->right=b->left; b->left=a; if(a->right!=0) a->right->parent=a; setBalance(a); setBalance(b); return b; } struct AVLNode* turnRight(AVLNode* a){ AVLNode* b=a->left; if(a->parent!=0){ if(a->parent->right==a){ a->parent->right=b; }else{ a->parent->left=b; } } b->parent=a->parent; a->parent=b; a->left=b->right; if(a->left!=0) a->left->parent=a; b->right=a; setBalance(a); setBalance(b); return b; } struct AVLNode* turnLeftThenRight(AVLNode* a){ a->left = turnLeft(a->left); return turnRight(a); } struct AVLNode* turnRightThenLeft(AVLNode* a){ a->right = turnRight(a->right); return turnLeft(a); } void rebalance(AVLNode* a){ setBalance(a); if(a->balance== -2){ if(a->left->balance <=0){ a=turnRight(a); }else{ a=turnLeftThenRight(a); } }else if(a->balance==2){ if(a->right->balance>=0){ a=turnLeft(a); }else{ a=turnRightThenLeft(a); } } if(a->parent){ rebalance(a->parent); }else{ tree.root=a; } } void insert(int value,AVLNode* root){ AVLNode* node=select(value,root); if(node==0){ tree.root=(AVLNode*)malloc(sizeof(AVLNode)); tree.root->value=value; tree.root->left=tree.root->right=0; tree.root->parent=0; tree.root->balance=0; } else if(node->value!=value){ if(node->value>value){ node->left=(AVLNode*)malloc(sizeof(AVLNode)); node->left->value=value; node->left->left=node->left->right=0; node->left->parent=node; node->left->balance=0; rebalance(node); }else if(node->value<value){ node->right=(AVLNode*)malloc(sizeof(AVLNode)); node->right->value=value; node->right->left=node->right->right=0; node->right->parent=node; node->right->balance=0; rebalance(node); } } } void delnodeifhas1childornot(AVLNode* a){ if(a->parent==0){ if(a->left){ tree.root=a->left; a->left->parent=0; }else{ tree.root=a->right; a->right->parent=0; } }else{ if(a->parent->left==a){ if(a->left){ a->parent->left=a->left; a->left->parent=a->parent; }else{ a->parent->left=a->right; if(a->right) a->right->parent=a->parent; } }else{ if(a->left){ a->parent->right=a->left; a->left->parent=a->parent; }else{ a->parent->right=a->right; if(a->right) a->right->parent=a->parent; } } rebalance(a->parent); } } struct AVLNode* getmin(AVLNode* a){ if(a->left) getmin(a->left); else return a; } void delnodeifhas2child(AVLNode* a){ AVLNode* after=getmin(a->right); a->value=after->value; delnodeifhas1childornot(after); } void del(int value,AVLNode* root){ AVLNode* node=select(value,root); if(node->value==value){ if(node->left&&node->right){ delnodeifhas2child(node); }else{ delnodeifhas1childornot(node); } } } void insertNodetest(){ insert(5,tree.root); insert(6,tree.root); insert(7,tree.root); insert(3,tree.root); insert(4,tree.root); insert(4,tree.root); insert(5,tree.root); insert(5,tree.root); insert(3,tree.root); insert(8,tree.root); insert(9,tree.root); insert(10,tree.root); insert(11,tree.root); insert(12,tree.root); } void delNodetest(){ del(6,tree.root); del(11,tree.root); del(12,tree.root); del(3,tree.root); del(3,tree.root); del(5,tree.root); del(9,tree.root); del(12,tree.root); } int main(){ insertNodetest(); // 测试构建AVL树 delNodetest(); // 测试删除AVL树节点 }
至此,我们就完全实现了一颗AVL树的插入删除和查询功能。
(欢迎加qq:1363890602,讨论qq群:297572046,备注:编程艺术)
标签:node,right,parent,手把手,AVLNode,AVL,手写,root,left 来源: https://www.cnblogs.com/coding-nerver-die/p/10975599.html