AVL平衡树的理解(插入、查找、删除)
-
Dargon
-
2020/11/21
-
所遇到的的重要的问题:
-
教科书 来自:《数据结构和算法分析》第四章 树
AVL平衡树出现的原因
- 在一般的二叉树的插入和删除操作中,想想出现最坏的情况,
- 例如以20-26的顺讯进行插入的话,就类似形成一个线性链表的结构,让树的深度变得很深,不易搜索
- 进行删除操作的时候,一般过程,找到需要删除的节点,然后,找到该节点左子树的最大值,将其值保存下来,进行删除,把值更新到原本需要删除的节点,这样完成一次删除操作。久而久之,进行大量的删除、插入操作会出现树的不平衡往左侧。
关于节点的旋转
左单旋转
结合图进行观看,很直观
static AvlNode *single_rotate_with_left(AvlNode *k2) {
AvlNode *k1;
k1 =k2->left;
k2->left =k1->right;
k1->right =k2;
k2->height =( Max( height_avltree(k2->left), height_avltree(k2->right) ) ) +1;
k1->height =( Max( height_avltree(k1->left), k2->height ) ) +1;
return k1; //return the new root node
}
右单旋转
基本和左单旋转是对称的
static AvlNode *single_rotate_with_right(AvlNode *k2) {
AvlNode *k1;
k1 =k2->right;
k2->right =k1->left;
k1->left =k2;
k2->height =( Max( height_avltree(k2->left), height_avltree(k2->right) ) ) +1;
k1->height =( Max( height_avltree(k1->right), k2->height ) ) +1;
return k1; //return the new root node
}
左双旋转
实际分析是进行两次旋转,先进行右旋转,之后左旋转
static AvlNode *double_rotate_with_left(AvlNode *k3) {
k3->left =single_rotate_with_right(k3->left);
return single_rotate_with_left(k3);
}
右双旋转
先进行左旋转,之后进行右旋转
static AvlNode *double_rotate_with_right(AvlNode *k3) {
k3->right =single_rotate_with_left(k3->right);
return single_rotate_with_right(k3);
}
树的插入
直接看代码,正常插入,遇见不平衡(一个节点的左右子树高度差为 2,就是不平衡的)进行旋转调整
考虑四种情况:
- 左边的左边 形成
/
形状 - 左边的右边 形成
<
形状 - 右边的左边 形成
>
形状 - 右边的右边 形成
\
形状
1 和 4 对应的一次单旋转即可
2 和 3 对应的是双旋转
AvlNode *insert_avlnode(TREE_TYPE value, AvlNode *tree) {
if(tree ==NULL) {
tree =(AvlNode *)malloc(sizeof(AvlNode));
assert( tree != NULL );
tree->value =value;
tree->height =0;
tree->left =tree->right =NULL;
}
else if(value <tree->value) { //在左边插入
tree->left =insert_avlnode(value, tree->left);
//判断当前节点,左右子树的高度差,一旦该值为2,就是不平衡了,需要旋转调整
if( ((height_avltree(tree->left)) -(height_avltree(tree->right))) ==2 ) {
if( value <tree->left->value ) { //这里形成 “/” 形状 需要单左旋转
tree =single_rotate_with_left(tree);
}
else { //这里形成 “<” 形状 需要左-右双旋转
tree =double_rotate_with_left(tree);
}
}
}
else if(value >tree->value) { //在右边插入
tree->right =insert_avlnode(value, tree->right);
if( ((height_avltree(tree->right)) -(height_avltree(tree->left))) ==2 ) {
if( value >tree->right->value ) { //这里形成 “\” 形状 需要单右旋转
tree =single_rotate_with_right(tree);
}
else { //这里形成 “>” 形状 需要右-左双旋转
tree =double_rotate_with_right(tree);
}
}
}
else {
//value already exist .So do nothing !
}
tree->height =(Max( height_avltree(tree->left), height_avltree(tree->right) )) +1;
return tree;
}
树的查找
运用遍历(traverse)很方便的查找
AvlNode *find_avlnode( AvlNode *tree, TREE_TYPE value ) {
if(tree ==NULL) {
printf("Tree is NULL\n");
return NULL;
}
if(value <tree->value) {
return find_avlnode(tree->left, value);
}
else if(value >tree->value) {
return find_avlnode(tree->right, value);
}
else {
return tree;
}
}
树的删除
关于删除右边元素的情况,两种复杂情况的画图分析(左边和其类似)
AvlNode *delete_avlnode( AvlNode *tree, TREE_TYPE value ) {
AvlNode *temp =NULL;
AvlNode **link;
link =&avltree;
if(tree ==NULL) {
printf("节点不存在\n");
return NULL;
}
if( value >tree->value ) { //目标值大,去树的右边寻找
tree->right =delete_avlnode( tree->right, value ); //删除之后 进行调整
//判断当前节点,左右子树的高度差,一旦该值为2,就是不平衡了,需要旋转调整
tree->height =(Max( height_avltree(tree->left), height_avltree(tree->right) )) +1;
if( height_avltree(tree->left) -height_avltree(tree->right) ==2 ) {
if( height_avltree(tree->left->left) >=height_avltree(tree->left->right) ) {
//删除右边节点,导致左边高度差大,这里形成 “/” 形状 需要单右旋转
tree =single_rotate_with_left(tree);
}
else { //这里形成 “<” 形状 需要右-左双旋转
tree =double_rotate_with_left(tree);
}
}
}
else if( value <tree->value ) { //目标值小,去树的左边寻找
tree->left =delete_avlnode( tree->left, value );
tree->height =(Max( height_avltree(tree->left), height_avltree(tree->right) )) +1;
if( (height_avltree(tree->right) -height_avltree(tree->left)) ==2 ) {
if( height_avltree(tree->right->left) <=height_avltree(tree->right->right) ) { //这里形成 “\” 形状 需要单左旋转
tree =single_rotate_with_right(tree);
}
else { //这里形成 “>” 形状 需要左-右双旋转
tree =double_rotate_with_right(tree);
}
}
}
else if( value ==tree->value ) { //利用递归 最终找到目标值(删除节点)
temp =tree;
if( tree->left ==NULL && tree->right ==NULL ) { //删除节点为叶节点
tree =NULL;
free(temp);
}
else if( tree->left !=NULL && tree->right ==NULL ) { //删除节点只有左边有子树
tree =tree->left;
free(temp);
}
else if( tree->left ==NULL && tree->right !=NULL ) { //删除节点只有右边有子树
tree =tree->right;
free(temp);
}
else { //删除节点两边都有子树
temp =find_min_avlnode(tree->right);
tree->value =temp->value;
tree->right =delete_avlnode(tree->right, tree->value);
}
}
return tree;
}
总结
- 在学习两天的AVL 树的理解,这些比较用脑子,保持着思考就行,再此记录一下,以后方便知识的回顾,遇到事情,多思考!有些事情来了,就接着,愁眉苦脸垂头丧气永远解决不了问题的!加油!